mirror of
https://github.com/geekwenjie/SmartJavaAI.git
synced 2026-09-20 01:29:18 +00:00
集成算法seetaface6
This commit is contained in:
@@ -0,0 +1,319 @@
|
||||
package cn.smartjavaai.face.algo;
|
||||
|
||||
import cn.smartjavaai.common.entity.Point;
|
||||
import cn.smartjavaai.common.entity.Rectangle;
|
||||
import cn.smartjavaai.common.utils.ImageUtils;
|
||||
import cn.smartjavaai.face.AbstractFaceAlgorithm;
|
||||
import cn.smartjavaai.face.FaceDetectedResult;
|
||||
import cn.smartjavaai.face.ModelConfig;
|
||||
import cn.smartjavaai.face.dao.FaceDao;
|
||||
import cn.smartjavaai.face.entity.FaceData;
|
||||
import cn.smartjavaai.face.entity.FaceResult;
|
||||
import com.seetaface.NativeLoader;
|
||||
import com.seetaface.SeetaFace6JNI;
|
||||
import com.seetaface.model.RecognizeResult;
|
||||
import com.seetaface.model.SeetaImageData;
|
||||
import com.seetaface.model.SeetaRect;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* SeetaFace6 人脸算法
|
||||
* @author dwj
|
||||
*/
|
||||
@Slf4j
|
||||
public class SeetaFace6Algo extends AbstractFaceAlgorithm {
|
||||
|
||||
|
||||
private ModelConfig config;
|
||||
|
||||
|
||||
@Override
|
||||
public void loadModel(ModelConfig config) throws Exception {
|
||||
this.config = config;
|
||||
if (NativeLoader.seetaFace6SDK == null) {
|
||||
synchronized (SeetaFace6JNI.class) {
|
||||
if(StringUtils.isBlank(config.getModelPath())){
|
||||
throw new Exception("modelPath is null");
|
||||
}
|
||||
//加载依赖库
|
||||
NativeLoader.loadNativeLibraries(config.getModelPath());
|
||||
log.info("Loading seetaFace6 library successfully.");
|
||||
NativeLoader.seetaFace6SDK = new SeetaFace6JNI();
|
||||
//加载模型
|
||||
boolean isSuccess = NativeLoader.seetaFace6SDK.initModel(config.getModelPath());
|
||||
if(!isSuccess){
|
||||
throw new Exception("seetaFace6模型初始化失败," + config.getModelPath());
|
||||
}
|
||||
log.info("Load seetaFace6 model success!");
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
log.info("start load faceDb...");
|
||||
loadFaceDb();
|
||||
log.info("Load faceDb success!");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaceDetectedResult detect(String imagePath) throws Exception {
|
||||
// 将图片路径转换为 BufferedImage
|
||||
BufferedImage image = ImageIO.read(new File(Paths.get(imagePath).toAbsolutePath().toString()));
|
||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||
imageData.data = ImageUtils.getMatrixBGR(image);
|
||||
SeetaRect[] seetaResult = NativeLoader.seetaFace6SDK.detect(imageData);
|
||||
return convertToFaceDetectedResult(seetaResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaceDetectedResult detect(InputStream imageInputStream) throws Exception {
|
||||
BufferedImage image = ImageIO.read(imageInputStream);
|
||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||
imageData.data = ImageUtils.getMatrixBGR(image);
|
||||
SeetaRect[] seetaResult = NativeLoader.seetaFace6SDK.detect(imageData);
|
||||
return convertToFaceDetectedResult(seetaResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] featureExtraction(String imagePath) throws Exception {
|
||||
BufferedImage image = ImageIO.read(new File(Paths.get(imagePath).toAbsolutePath().toString()));
|
||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||
imageData.data = ImageUtils.getMatrixBGR(image);
|
||||
return NativeLoader.seetaFace6SDK.extractMaxFace(imageData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] featureExtraction(InputStream inputStream) throws Exception {
|
||||
BufferedImage image = ImageIO.read(inputStream);
|
||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||
imageData.data = ImageUtils.getMatrixBGR(image);
|
||||
return NativeLoader.seetaFace6SDK.extractMaxFace(imageData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float calculSimilar(float[] feature1, float[] feature2) throws Exception {
|
||||
return NativeLoader.seetaFace6SDK.calculateSimilarity(feature1, feature2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float featureComparison(String imagePath1, String imagePath2) throws Exception {
|
||||
return featureComparison(new FileInputStream(Paths.get(imagePath1).toAbsolutePath().toString()),
|
||||
new FileInputStream(Paths.get(imagePath2).toAbsolutePath().toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float featureComparison(InputStream inputStream1, InputStream inputStream2) throws Exception {
|
||||
BufferedImage image1 = ImageIO.read(inputStream1);
|
||||
BufferedImage image2 = ImageIO.read(inputStream2);
|
||||
SeetaImageData imageData1 = new SeetaImageData(image1.getWidth(), image1.getHeight(), 3);
|
||||
imageData1.data = ImageUtils.getMatrixBGR(image1);
|
||||
|
||||
SeetaImageData imageData2 = new SeetaImageData(image2.getWidth(), image2.getHeight(), 3);
|
||||
imageData2.data = ImageUtils.getMatrixBGR(image2);
|
||||
//裁剪
|
||||
byte[][] cropImg1 = NativeLoader.seetaFace6SDK.crop(imageData1);
|
||||
byte[][] cropImg2 = NativeLoader.seetaFace6SDK.crop(imageData2);
|
||||
if(cropImg1 == null || cropImg1.length == 0){
|
||||
throw new Exception("未发现人脸");
|
||||
}
|
||||
if(cropImg2 == null || cropImg2.length == 0){
|
||||
throw new Exception("未发现人脸");
|
||||
}
|
||||
|
||||
BufferedImage cropImage1 = ImageUtils.bgrToBufferedImage(cropImg1[0], 256, 256);
|
||||
BufferedImage cropImage2 = ImageUtils.bgrToBufferedImage(cropImg2[0], 256, 256);
|
||||
|
||||
SeetaImageData cropImageData1 = new SeetaImageData(cropImage1.getWidth(), cropImage1.getHeight(), 3);
|
||||
cropImageData1.data = ImageUtils.getMatrixBGR(cropImage1);
|
||||
SeetaImageData cropImageData2 = new SeetaImageData(cropImage2.getWidth(), cropImage2.getHeight(), 3);
|
||||
cropImageData2.data = ImageUtils.getMatrixBGR(cropImage2);
|
||||
return NativeLoader.seetaFace6SDK.compare(cropImageData1, cropImageData2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 转换为FaceDetectedResult
|
||||
* @param seetaResult
|
||||
* @return
|
||||
*/
|
||||
private FaceDetectedResult convertToFaceDetectedResult(SeetaRect[] seetaResult){
|
||||
FaceDetectedResult faceDetectedResult = new FaceDetectedResult();
|
||||
List<Rectangle> RectangleList = new ArrayList<Rectangle>();
|
||||
List<Double> probabilities = new ArrayList<Double>();
|
||||
if(seetaResult != null && seetaResult.length > 0){
|
||||
for(SeetaRect rect : seetaResult){
|
||||
Rectangle rectangle = new Rectangle();
|
||||
List<Point> pointList = new ArrayList<>();
|
||||
pointList.add(new Point(rect.x,rect.y));
|
||||
pointList.add(new Point(rect.x + rect.width,rect.y));
|
||||
pointList.add(new Point(rect.x,rect.y + rect.height));
|
||||
pointList.add(new Point(rect.x + rect.width,rect.y + rect.height));
|
||||
rectangle.setPointList(pointList);
|
||||
rectangle.setHeight(rect.height);
|
||||
rectangle.setWidth(rect.width);
|
||||
RectangleList.add(rectangle);
|
||||
probabilities.add(new Double(rect.score));
|
||||
}
|
||||
}
|
||||
faceDetectedResult.setProbabilities(probabilities);
|
||||
faceDetectedResult.setRectangles(RectangleList);
|
||||
return faceDetectedResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean register(String key, String imagePath) throws Exception {
|
||||
return register(key, new FileInputStream(Paths.get(imagePath).toAbsolutePath().toString()));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean register(String key, InputStream inputStream) throws Exception {
|
||||
if(!checkFaceDb()){
|
||||
throw new Exception("未找到人脸库,无法使用此功能(请检查是否配置人脸库路径)");
|
||||
}
|
||||
//裁剪人脸
|
||||
BufferedImage image = ImageIO.read(inputStream);
|
||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||
imageData.data = ImageUtils.getMatrixBGR(image);
|
||||
byte[][] bytes = NativeLoader.seetaFace6SDK.crop(imageData);
|
||||
if (bytes == null || bytes.length == 0) {
|
||||
log.info("register face fail: key={}, error=no valid face", key);
|
||||
return false;
|
||||
}
|
||||
long index = NativeLoader.seetaFace6SDK.registerCroppedFace(bytes[0]);
|
||||
if (index < 0) {
|
||||
log.info("register face fail: key={}, index={}", key, index);
|
||||
return false;
|
||||
}
|
||||
//持久化到sqlite数据库
|
||||
FaceData face = new FaceData();
|
||||
face.setKey(key);
|
||||
face.setIndex(index);
|
||||
face.setImgData(bytes[0]);
|
||||
new FaceDao(config.getFaceDbPath()).save(face);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean register(String key, FaceData faceData) throws Exception {
|
||||
long index = NativeLoader.seetaFace6SDK.registerCroppedFace(faceData.getImgData());
|
||||
if (index < 0) {
|
||||
log.info("register face fail: key={}, index={}", key, index);
|
||||
return false;
|
||||
}
|
||||
int rows = new FaceDao(config.getFaceDbPath()).updateIndex(index, faceData);
|
||||
return rows > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public FaceResult search(String imagePath) throws Exception {
|
||||
return search(new FileInputStream(Paths.get(imagePath).toAbsolutePath().toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FaceResult search(InputStream inputStream) throws Exception{
|
||||
if(!checkFaceDb()){
|
||||
throw new Exception("未找到人脸库,无法使用此功能(请检查是否配置人脸库路径)");
|
||||
}
|
||||
BufferedImage image = ImageIO.read(inputStream);
|
||||
SeetaImageData imageData = new SeetaImageData(image.getWidth(), image.getHeight(), 3);
|
||||
imageData.data = ImageUtils.getMatrixBGR(image);
|
||||
RecognizeResult recognizeResult = NativeLoader.seetaFace6SDK.query(imageData);
|
||||
return searchFaceDb(recognizeResult);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public long removeRegister(String... keys) throws Exception {
|
||||
if(!checkFaceDb()){
|
||||
throw new Exception("未找到人脸库,无法使用此功能(请检查是否配置人脸库路径)");
|
||||
}
|
||||
List<Long> list = new FaceDao(config.getFaceDbPath()).findIndexList(keys);
|
||||
if (list == null) {
|
||||
return 0;
|
||||
}
|
||||
long[] array = list.stream().mapToLong(Long::longValue).toArray();
|
||||
long rows = NativeLoader.seetaFace6SDK.delete(array);
|
||||
new FaceDao(config.getFaceDbPath()).deleteFace(keys);
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long clearFace() throws Exception{
|
||||
if(!checkFaceDb()){
|
||||
throw new Exception("未找到人脸库,无法使用此功能(请检查是否配置人脸库路径)");
|
||||
}
|
||||
long rows = NativeLoader.seetaFace6SDK.delete(new long[]{-1});
|
||||
new FaceDao(config.getFaceDbPath()).deleteAll();
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否存在人脸库
|
||||
* @return
|
||||
*/
|
||||
private boolean checkFaceDb(){
|
||||
if(Objects.nonNull(config) && StringUtils.isNotBlank(config.getFaceDbPath())){
|
||||
File file = new File(config.getFaceDbPath());
|
||||
return file.exists() && file.isFile();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private FaceResult searchFaceDb(RecognizeResult recognizeResult) throws SQLException, ClassNotFoundException {
|
||||
if(recognizeResult != null && recognizeResult.index >= 0){
|
||||
String key = new FaceDao(config.getFaceDbPath()).findKeyByIndex(recognizeResult.index);
|
||||
return new FaceResult(key, recognizeResult.similar);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 加载人脸库
|
||||
* @throws SQLException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
private void loadFaceDb() throws SQLException, ClassNotFoundException {
|
||||
if(!checkFaceDb()){
|
||||
log.info("未配置人脸库");
|
||||
return;
|
||||
}
|
||||
//分页查询人脸库
|
||||
int pageNo = 0, pageSize = 100;
|
||||
while (true) {
|
||||
List<FaceData> list = new FaceDao(config.getFaceDbPath()).findFace(pageNo, pageSize);
|
||||
if (list == null) {
|
||||
break;
|
||||
}
|
||||
list.forEach(face -> {
|
||||
try {
|
||||
register(face.getKey(), face);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
if (list.size() < pageSize) {
|
||||
break;
|
||||
}
|
||||
pageNo++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user