diff --git a/docs/api/video-api.md b/docs/api/video-api.md index 3c53ebc..971ecac 100644 --- a/docs/api/video-api.md +++ b/docs/api/video-api.md @@ -1,9 +1,378 @@ -# 视频模块接口文档 +\# 视频模块接口文档 + +\## 基础信息 -## 基础信息 - 基础路径: `/api/videos` - 请求头: 需要携带 `Authorization: Bearer {token}` (除了获取视频列表和视频详情) -## 接口列表 +\## 接口列表 -### 1. 上传视频 \ No newline at end of file +\### 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 // 视频列表 + +} + +\### 9. 获取相似视频 + +GET /api/videos/{id}/similar?limit=10 + +// 请求参数 (query) + +{ + +limit?: number // 返回数量,默认10 + +} + +// 响应 + +{ + +code: number, + +message: string, + +data: Array // 视频列表 + +} + +\## 错误码说明 + +{ + +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; // 当前用户是否已点赞 + +} diff --git a/docs/api/video-api.txt b/docs/api/video-api.txt index 82353b9..bc8e3ae 100644 --- a/docs/api/video-api.txt +++ b/docs/api/video-api.txt @@ -2,7 +2,7 @@ ## 基础信息 - 基础路径: `/api/videos` -- 请求头: 需要携带 `Authorization: Bearer {token}` (除了获取视频列表和视频详情) + - 请求头: 需要携带 `Authorization: Bearer {token}` (除了获取视频列表和视频详情) ## 接口列表 diff --git a/pom.xml b/pom.xml index 2a177c3..de918dd 100644 --- a/pom.xml +++ b/pom.xml @@ -214,6 +214,15 @@ 2.3.0 + + + com.arcsoft.face + arcsoft-sdk-face + 4.1.1.0 + + + + diff --git a/scripts/train_recommend_model.py b/scripts/train_recommend_model.py deleted file mode 100644 index 19f382a..0000000 --- a/scripts/train_recommend_model.py +++ /dev/null @@ -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() \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/Handler/MyMetaObjectHandler.java b/src/main/java/com/guwan/backend/Handler/MyMetaObjectHandler.java index 94ee477..cd2e87f 100644 --- a/src/main/java/com/guwan/backend/Handler/MyMetaObjectHandler.java +++ b/src/main/java/com/guwan/backend/Handler/MyMetaObjectHandler.java @@ -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 diff --git a/src/main/java/com/guwan/backend/JwtExample.java b/src/main/java/com/guwan/backend/JwtExample.java deleted file mode 100644 index 64b05a6..0000000 --- a/src/main/java/com/guwan/backend/JwtExample.java +++ /dev/null @@ -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 - } -} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/config/SwaggerConfig.java b/src/main/java/com/guwan/backend/config/SwaggerConfig.java index 5ff933d..a5ac0c6 100644 --- a/src/main/java/com/guwan/backend/config/SwaggerConfig.java +++ b/src/main/java/com/guwan/backend/config/SwaggerConfig.java @@ -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 diff --git a/src/main/java/com/guwan/backend/constant/SecurityConstants.java b/src/main/java/com/guwan/backend/constant/SecurityConstants.java index 57598ad..f5d6592 100644 --- a/src/main/java/com/guwan/backend/constant/SecurityConstants.java +++ b/src/main/java/com/guwan/backend/constant/SecurityConstants.java @@ -31,7 +31,7 @@ public class SecurityConstants { public static final List STATIC_RESOURCES = List.of( "/static/**", // 静态资源目录 "/public/**", // 公共资源目录 - "/error", // 错误页面 - "/swagger-ui.html" + "/error" // 错误页面 + ); } \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/controller/DemoController.java b/src/main/java/com/guwan/backend/controller/DemoController.java index 66c1d90..d725b0d 100644 --- a/src/main/java/com/guwan/backend/controller/DemoController.java +++ b/src/main/java/com/guwan/backend/controller/DemoController.java @@ -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 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 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()); + + + } } diff --git a/src/main/java/com/guwan/backend/controller/VideoController.java b/src/main/java/com/guwan/backend/controller/VideoController.java index 93e22b9..55569ac 100644 --- a/src/main/java/com/guwan/backend/controller/VideoController.java +++ b/src/main/java/com/guwan/backend/controller/VideoController.java @@ -24,7 +24,7 @@ public class VideoController { @Operation(summary = "上传视频", description = "上传视频文件并返回视频信息") - @SecurityRequirement(name = "bearer-jwt") + // @SecurityRequirement(name = "bearer-jwt") @PostMapping("/upload") public Result uploadVideo( @Parameter(description = "视频文件") @RequestParam("file") MultipartFile file, diff --git a/src/main/java/com/guwan/backend/face/FaceEngineAutoRun.java b/src/main/java/com/guwan/backend/face/FaceEngineAutoRun.java new file mode 100644 index 0000000..e989cae --- /dev/null +++ b/src/main/java/com/guwan/backend/face/FaceEngineAutoRun.java @@ -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 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 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 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 cellList = tenCellService.list(new QueryWrapper() +// .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 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+" 人"); +// } +// } +//} diff --git a/src/main/java/com/guwan/backend/face/config/ArcFaceAutoConfiguration.java b/src/main/java/com/guwan/backend/face/config/ArcFaceAutoConfiguration.java new file mode 100644 index 0000000..c65bdd0 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/config/ArcFaceAutoConfiguration.java @@ -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 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 { + } + +} diff --git a/src/main/java/com/guwan/backend/face/dto/CompareFacesReqDTO.java b/src/main/java/com/guwan/backend/face/dto/CompareFacesReqDTO.java new file mode 100644 index 0000000..5f474a9 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/CompareFacesReqDTO.java @@ -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; +} diff --git a/src/main/java/com/guwan/backend/face/dto/FaceAddReqDTO.java b/src/main/java/com/guwan/backend/face/dto/FaceAddReqDTO.java new file mode 100644 index 0000000..6ba4b17 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/FaceAddReqDTO.java @@ -0,0 +1,12 @@ +package com.guwan.backend.face.dto; + +import lombok.Data; + +@Data +public class FaceAddReqDTO { + + private String name; + + private String image; + +} diff --git a/src/main/java/com/guwan/backend/face/dto/FaceDetectReqDTO.java b/src/main/java/com/guwan/backend/face/dto/FaceDetectReqDTO.java new file mode 100644 index 0000000..e8dbff0 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/FaceDetectReqDTO.java @@ -0,0 +1,10 @@ +package com.guwan.backend.face.dto; + +import lombok.Data; + +@Data +public class FaceDetectReqDTO { + + private String image; + +} diff --git a/src/main/java/com/guwan/backend/face/dto/FaceDetectResDTO.java b/src/main/java/com/guwan/backend/face/dto/FaceDetectResDTO.java new file mode 100644 index 0000000..1f94ed7 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/FaceDetectResDTO.java @@ -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; +} diff --git a/src/main/java/com/guwan/backend/face/dto/FaceRecognitionReqDTO.java b/src/main/java/com/guwan/backend/face/dto/FaceRecognitionReqDTO.java new file mode 100644 index 0000000..de0b1dc --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/FaceRecognitionReqDTO.java @@ -0,0 +1,10 @@ +package com.guwan.backend.face.dto; + +import lombok.Data; + +@Data +public class FaceRecognitionReqDTO { + + private String image; + +} diff --git a/src/main/java/com/guwan/backend/face/dto/FaceRecognitionResDTO.java b/src/main/java/com/guwan/backend/face/dto/FaceRecognitionResDTO.java new file mode 100644 index 0000000..ee9f031 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/FaceRecognitionResDTO.java @@ -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; + +} diff --git a/src/main/java/com/guwan/backend/face/dto/GetFaceListResDTO.java b/src/main/java/com/guwan/backend/face/dto/GetFaceListResDTO.java new file mode 100644 index 0000000..7130ddb --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/GetFaceListResDTO.java @@ -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; + +} diff --git a/src/main/java/com/guwan/backend/face/entity/ProcessInfo.java b/src/main/java/com/guwan/backend/face/entity/ProcessInfo.java new file mode 100644 index 0000000..dac68fa --- /dev/null +++ b/src/main/java/com/guwan/backend/face/entity/ProcessInfo.java @@ -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; + +} diff --git a/src/main/java/com/guwan/backend/face/entity/UserCompareInfo.java b/src/main/java/com/guwan/backend/face/entity/UserCompareInfo.java new file mode 100644 index 0000000..38ec540 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/entity/UserCompareInfo.java @@ -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是 +} diff --git a/src/main/java/com/guwan/backend/face/enums/ErrorCodeEnum.java b/src/main/java/com/guwan/backend/face/enums/ErrorCodeEnum.java new file mode 100644 index 0000000..db93662 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/enums/ErrorCodeEnum.java @@ -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; + } + +} diff --git a/src/main/java/com/guwan/backend/face/face/FacePreviewInfo.java b/src/main/java/com/guwan/backend/face/face/FacePreviewInfo.java new file mode 100644 index 0000000..2e8dd88 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/face/FacePreviewInfo.java @@ -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; + } + +} diff --git a/src/main/java/com/guwan/backend/face/face/FaceRecognize.java b/src/main/java/com/guwan/backend/face/face/FaceRecognize.java new file mode 100644 index 0000000..7ce3510 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/face/FaceRecognize.java @@ -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 frEnginePool; + + + private static volatile ConcurrentHashMap faceResultRegistry = new ConcurrentHashMap<>(); + + private static ExecutorService frService = Executors.newFixedThreadPool(20); + + public static ConcurrentHashMap 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 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 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 detectFaces(ImageInfo imageInfo) { + if (ftEngine != null) { + List faceInfoList = new ArrayList<>(); + int code = ftEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), + imageInfo.getImageFormat(), faceInfoList); + + List 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 faceInfoList) { + if (System.currentTimeMillis() - lastClearTime > 5000) { + Iterator 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> iterator = faceFeatureRegistry.entrySet().iterator(); + for (; iterator.hasNext(); ) { + Map.Entry 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); + } + } + + + } + } + + +} diff --git a/src/main/java/com/guwan/backend/face/factory/FaceEngineFactory.java b/src/main/java/com/guwan/backend/face/factory/FaceEngineFactory.java new file mode 100644 index 0000000..84ce156 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/factory/FaceEngineFactory.java @@ -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 { + + 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 wrap(FaceEngine faceEngine) { + return new DefaultPooledObject<>(faceEngine); + } + + + @Override + public void destroyObject(PooledObject p) throws Exception { + FaceEngine faceEngine = p.getObject(); + int result = faceEngine.unInit(); + super.destroyObject(p); + } +} diff --git a/src/main/java/com/guwan/backend/face/rpc/BusinessException.java b/src/main/java/com/guwan/backend/face/rpc/BusinessException.java new file mode 100644 index 0000000..efad3b0 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/rpc/BusinessException.java @@ -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; + } +} diff --git a/src/main/java/com/guwan/backend/face/rpc/ErrorCode.java b/src/main/java/com/guwan/backend/face/rpc/ErrorCode.java new file mode 100644 index 0000000..ceb2dec --- /dev/null +++ b/src/main/java/com/guwan/backend/face/rpc/ErrorCode.java @@ -0,0 +1,12 @@ +package com.guwan.backend.face.rpc; + +public interface ErrorCode { + + + Integer getCode(); + + String getDesc(); + + String getDescCN(); + +} diff --git a/src/main/java/com/guwan/backend/face/rpc/GlobalExceptionHandler.java b/src/main/java/com/guwan/backend/face/rpc/GlobalExceptionHandler.java new file mode 100644 index 0000000..a189f51 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/rpc/GlobalExceptionHandler.java @@ -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 handleInternalServerErrorException(InternalServerErrorException ex) { +//// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage()); +//// } +// +// +// +// +//} diff --git a/src/main/java/com/guwan/backend/face/rpc/Response.java b/src/main/java/com/guwan/backend/face/rpc/Response.java new file mode 100644 index 0000000..2b4ac76 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/rpc/Response.java @@ -0,0 +1,39 @@ +package com.guwan.backend.face.rpc; + +import lombok.Data; + +@Data +public class Response { + + private int code = -1; + private String msg = "success"; + private T data; + + public static Response newSuccessResponse(T data) { + return newResponse(data, 0, "success"); + } + + public static Response newFailedResponse(Integer code, String message) { + return newResponse(null, code, message); + } + + public static Response newFailedResponse(ErrorCode ErrorCode) { + return newResponse(null, ErrorCode.getCode(), ErrorCode.getDesc()); + } + + public static Response newFailedResponse(ErrorCode ErrorCode, String message) { + return newResponse(null, ErrorCode.getCode(), message); + } + + public static Response newResponse(T data, Integer code, String message) { + Response response = new Response(); + response.setCode(code); + response.setMsg(message); + if (data != null && data instanceof String && "".equals(data)) { + response.setData(null); + } else { + response.setData(data); + } + return response; + } +} diff --git a/src/main/java/com/guwan/backend/face/rtsp/RtspFrameGrabber.java b/src/main/java/com/guwan/backend/face/rtsp/RtspFrameGrabber.java new file mode 100644 index 0000000..c707add --- /dev/null +++ b/src/main/java/com/guwan/backend/face/rtsp/RtspFrameGrabber.java @@ -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 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 startGrabber(String cellId) { +// +// List 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 recognition(MultipartFile file,String cellId) { +// try { +// byte[] bytes = file.getBytes(); +// return imageRecognitionNew(bytes, cellId); +// +// } catch (IOException e) { +// throw new RuntimeException(e); +// } +// } +// +// private List imageRecognition(byte[] bytes,String cellId) { +// List temp = new ArrayList<>(); +// if (bytes != null && bytes.length > 0) { +// ImageInfo imageInfo = ImageFactory.getRGBData(bytes); +// List 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 userInfoList = UserRamGroup.getUserList(cellId); +// //人脸对比 这里长度永远为1 +// List 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() +// .eq(TenPersonEntity::getPersonId, userCompareInfoList.get(0).getFaceId())); +// Map 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 params = new HashMap<>(); +// params.put("pmWatchVideoRecordId", ""); +// params.put("orgId", tenPerson.getOrgId()); +// params.put("orgName", tenCellDao.selectOne(new QueryWrapper().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() +// .set(TenPersonEntity::getIsWatchSafeVideo, 1) +// .eq(TenPersonEntity::getPersonId, userCompareInfoList.get(0).getFaceId())); +// } +// } +// +// +// } else { +// log.error("图片不合格,未检测到人脸"); +// } +// }); +// +// } else { +// log.error("图片不合格,未检测到人脸"); +// } +// +// } else { +// } +// return temp; +// } +// +// +// private List imageRecognitionNew(byte[] bytes,String cellId) { +// List temp = new ArrayList<>(); +// if (bytes != null && bytes.length > 0) { +// ImageInfo imageInfo = ImageFactory.getRGBData(bytes); +// List 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 userInfoList = UserRamGroup.getUserList(cellId); +// //人脸对比 这里长度永远为1 +// List 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(); +// } +// +//} diff --git a/src/main/java/com/guwan/backend/face/service/FaceEngineService.java b/src/main/java/com/guwan/backend/face/service/FaceEngineService.java new file mode 100644 index 0000000..df48d33 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/service/FaceEngineService.java @@ -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 detectFaces(ImageInfo imageInfo); + + Float compareFace(ImageInfo imageInfo1, ImageInfo imageInfo2) ; + + byte[] extractFaceFeature(ImageInfo imageInfo, FaceInfo faceInfo, ExtractType extractType); + + List faceRecognition(byte[] faceFeature, List userInfoList, float passRate) ; + + List process(ImageInfo imageInfo, List faceInfoList); + + + +} diff --git a/src/main/java/com/guwan/backend/face/service/impl/FaceEngineServiceImpl.java b/src/main/java/com/guwan/backend/face/service/impl/FaceEngineServiceImpl.java new file mode 100644 index 0000000..3fe4961 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/service/impl/FaceEngineServiceImpl.java @@ -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 faceEngineGeneralPool; + + //人脸比对引擎池 + private GenericObjectPool 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 detectFaces(ImageInfo imageInfo) { + + FaceEngine faceEngine = null; + try { + faceEngine = faceEngineGeneralPool.borrowObject(); + if (faceEngine == null) { + throw new BusinessException(ErrorCodeEnum.FAIL, "获取引擎失败"); + } + + //人脸检测得到人脸列表 + List faceInfoList = new ArrayList(); + //人脸检测 + 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 faceInfoList1 = detectFaces(imageInfo1); + List 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 faceRecognition(byte[] faceFeature, List userInfoList, float passRate) { + List resultUserInfoList = Lists.newLinkedList();//识别到的人脸列表 + + FaceFeature targetFaceFeature = new FaceFeature(); + targetFaceFeature.setFeatureData(faceFeature); + + List> faceUserInfoPartList = Lists.partition(userInfoList, 1000);//分成1000一组,多线程处理 + CompletionService> completionService = new ExecutorCompletionService(compareExecutorService); + for (List part : faceUserInfoPartList) { + completionService.submit(new CompareFaceTask(part, targetFaceFeature, passRate)); + } + for (int i = 0; i < faceUserInfoPartList.size(); i++) { + List 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 process(ImageInfo imageInfo, List 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 processInfoList = Lists.newLinkedList(); + + //性别列表 + List genderInfoList = new ArrayList(); + faceEngine.getGender(genderInfoList); + + //年龄列表 + List ageInfoList = new ArrayList(); + faceEngine.getAge(ageInfoList); + //活体结果列表 + List livenessInfoList = new ArrayList(); + 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> { + + private List userInfoList; + private FaceFeature targetFaceFeature; + private float passRate; + + + public CompareFaceTask(List userInfoList, FaceFeature targetFaceFeature, float passRate) { + this.userInfoList = userInfoList; + this.targetFaceFeature = targetFaceFeature; + this.passRate = passRate; + } + + @Override + public List call() throws Exception { + FaceEngine faceEngine = null; + List 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; + } + + } +} diff --git a/src/main/java/com/guwan/backend/face/util/Base64Util.java b/src/main/java/com/guwan/backend/face/util/Base64Util.java new file mode 100644 index 0000000..7b94ab7 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/util/Base64Util.java @@ -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; + + + } +} diff --git a/src/main/java/com/guwan/backend/face/util/FaceEngineTest.java b/src/main/java/com/guwan/backend/face/util/FaceEngineTest.java new file mode 100644 index 0000000..532cf55 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/util/FaceEngineTest.java @@ -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 faceInfoList = new ArrayList(); + 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 faceInfoList2 = new ArrayList(); + 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 genderInfoList = new ArrayList(); + errorCode = faceEngine.getGender(genderInfoList); + System.out.println("性别:" + genderInfoList.get(0).getGender()); + + //年龄检测 + List ageInfoList = new ArrayList(); + errorCode = faceEngine.getAge(ageInfoList); + System.out.println("年龄:" + ageInfoList.get(0).getAge()); + + //活体检测 + List livenessInfoList = new ArrayList(); + errorCode = faceEngine.getLiveness(livenessInfoList); + System.out.println("活体:" + livenessInfoList.get(0).getLiveness()); + + //口罩检测 + List maskInfoList = new ArrayList(); + errorCode = faceEngine.getMask(maskInfoList); + System.out.println("口罩:" + maskInfoList.get(0).getMask()); + + + //IR属性处理 + ImageInfo imageInfoGray = ImageFactory.getGrayData(new File("/home/huangyifang/gb/咸阳师范/10.jpg")); + List faceInfoListGray = new ArrayList(); + errorCode = faceEngine.detectFaces(imageInfoGray, faceInfoListGray); + + FunctionConfiguration configuration2 = new FunctionConfiguration(); + configuration2.setSupportIRLiveness(true); + errorCode = faceEngine.processIr(imageInfoGray, faceInfoListGray, configuration2); + //IR活体检测 + List 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 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 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(); + + + } +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/face/util/UserInfo.java b/src/main/java/com/guwan/backend/face/util/UserInfo.java new file mode 100644 index 0000000..23e4fc7 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/util/UserInfo.java @@ -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; +} diff --git a/src/main/java/com/guwan/backend/face/util/UserRamCache.java b/src/main/java/com/guwan/backend/face/util/UserRamCache.java new file mode 100644 index 0000000..e7ccfbf --- /dev/null +++ b/src/main/java/com/guwan/backend/face/util/UserRamCache.java @@ -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 USER_INFO_MAP = new ConcurrentHashMap<>(); + + private final Set 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 getUserList() { + List 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) { + + } + } +} diff --git a/src/main/java/com/guwan/backend/face/util/UserRamGroup.java b/src/main/java/com/guwan/backend/face/util/UserRamGroup.java new file mode 100644 index 0000000..d7ab8e6 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/util/UserRamGroup.java @@ -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 USER_RAM_GROUP_MAP = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap 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 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(); + } +} diff --git a/src/main/java/com/guwan/backend/service/impl/VideoServiceImpl.java b/src/main/java/com/guwan/backend/service/impl/VideoServiceImpl.java index bf1d3db..1ca67da 100644 --- a/src/main/java/com/guwan/backend/service/impl/VideoServiceImpl.java +++ b/src/main/java/com/guwan/backend/service/impl/VideoServiceImpl.java @@ -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; } diff --git a/src/main/java/com/guwan/backend/util/MinioUtil.java b/src/main/java/com/guwan/backend/util/MinioUtil.java index 4a7f9a5..1cf1077 100644 --- a/src/main/java/com/guwan/backend/util/MinioUtil.java +++ b/src/main/java/com/guwan/backend/util/MinioUtil.java @@ -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("?")); + } + + /** * 生成文件名 */ diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ebb1a6b..40f3d27 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -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 \ No newline at end of file + 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 \ No newline at end of file diff --git a/src/main/resources/libs/4.1/LINUX64/libarcsoft_face.so b/src/main/resources/libs/4.1/LINUX64/libarcsoft_face.so new file mode 100644 index 0000000..b4c51b2 Binary files /dev/null and b/src/main/resources/libs/4.1/LINUX64/libarcsoft_face.so differ diff --git a/src/main/resources/libs/4.1/LINUX64/libarcsoft_face_engine.so b/src/main/resources/libs/4.1/LINUX64/libarcsoft_face_engine.so new file mode 100644 index 0000000..e797602 Binary files /dev/null and b/src/main/resources/libs/4.1/LINUX64/libarcsoft_face_engine.so differ diff --git a/src/main/resources/libs/4.1/LINUX64/libarcsoft_face_engine_jni.so b/src/main/resources/libs/4.1/LINUX64/libarcsoft_face_engine_jni.so new file mode 100644 index 0000000..e61ecf4 Binary files /dev/null and b/src/main/resources/libs/4.1/LINUX64/libarcsoft_face_engine_jni.so differ diff --git a/src/main/resources/libs/4.1/LINUX64old/libarcsoft_face_engine.so b/src/main/resources/libs/4.1/LINUX64old/libarcsoft_face_engine.so new file mode 100644 index 0000000..cad9ed2 Binary files /dev/null and b/src/main/resources/libs/4.1/LINUX64old/libarcsoft_face_engine.so differ diff --git a/src/main/resources/libs/4.1/WIN64/libarcsoft_face.dll b/src/main/resources/libs/4.1/WIN64/libarcsoft_face.dll new file mode 100644 index 0000000..d0a4c97 Binary files /dev/null and b/src/main/resources/libs/4.1/WIN64/libarcsoft_face.dll differ diff --git a/src/main/resources/libs/4.1/WIN64/libarcsoft_face_engine.dll b/src/main/resources/libs/4.1/WIN64/libarcsoft_face_engine.dll new file mode 100644 index 0000000..db65b00 Binary files /dev/null and b/src/main/resources/libs/4.1/WIN64/libarcsoft_face_engine.dll differ diff --git a/src/main/resources/libs/4.1/WIN64/libarcsoft_face_engine_jni.dll b/src/main/resources/libs/4.1/WIN64/libarcsoft_face_engine_jni.dll new file mode 100644 index 0000000..0266b32 Binary files /dev/null and b/src/main/resources/libs/4.1/WIN64/libarcsoft_face_engine_jni.dll differ