【人脸识别】 新增多种人脸识别模型

【底层优化】 支持自由选择 OpenCV 或 BufferedImage 作为图像引擎

【通用图像】 全部模型启用 Image 输入,支持各类图片格式与 Image 的互转

【模型管理】 优化模型生命周期,关闭后可重新创建

【人脸识别】 支持在人脸查询结果中绘制姓名标注

【人脸检测】 新增人脸裁剪功能

【修复】 修复若干已知问题,提升系统稳定性
This commit is contained in:
dengwenjie
2025-10-02 16:26:42 +08:00
parent 1b50e2b943
commit dfa8cf9bb4
133 changed files with 6635 additions and 3532 deletions

View File

@@ -4,22 +4,20 @@ import ai.djl.MalformedModelException;
import ai.djl.engine.Engine;
import ai.djl.inference.Predictor;
import ai.djl.modality.cv.Image;
import ai.djl.modality.cv.ImageFactory;
import ai.djl.modality.cv.output.BoundingBox;
import ai.djl.modality.cv.output.DetectedObjects;
import ai.djl.repository.zoo.Criteria;
import ai.djl.repository.zoo.ModelNotFoundException;
import ai.djl.repository.zoo.ZooModel;
import cn.smartjavaai.common.cv.SmartImageFactory;
import cn.smartjavaai.common.entity.DetectionResponse;
import cn.smartjavaai.common.entity.R;
import cn.smartjavaai.common.pool.PredictorFactory;
import cn.smartjavaai.common.utils.BufferedImageUtils;
import cn.smartjavaai.common.utils.FileUtils;
import cn.smartjavaai.common.utils.ImageUtils;
import cn.smartjavaai.common.utils.OpenCVUtils;
import cn.smartjavaai.objectdetection.config.DetectorModelConfig;
import cn.smartjavaai.objectdetection.criteria.CriteriaBuilderFactory;
import cn.smartjavaai.objectdetection.exception.DetectionException;
import cn.smartjavaai.vision.utils.CategoryMaskFilter;
import cn.smartjavaai.vision.utils.DetectedObjectsFilter;
import cn.smartjavaai.vision.utils.DetectorUtils;
import lombok.extern.slf4j.Slf4j;
@@ -31,8 +29,6 @@ import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
@@ -86,23 +82,21 @@ public class DetectorModel implements AutoCloseable{
* @return
* @throws Exception
*/
@Deprecated
public DetectionResponse detect(String imagePath){
if(!FileUtils.isFileExists(imagePath)){
throw new DetectionException("图像文件不存在");
}
Image image = null;
try {
image = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
DetectedObjects detectedObjects = detect(image);
image = SmartImageFactory.getInstance().fromFile(Paths.get(imagePath));
DetectedObjects detectedObjects = detectCore(image);
return DetectorUtils.convertToDetectionResponse(detectedObjects, image);
} catch (Exception e) {
throw new DetectionException(e);
} finally {
if (image != null){
((Mat)image.getWrappedImage()).release();
}
ImageUtils.releaseOpenCVMat(image);
}
}
@@ -117,8 +111,8 @@ public class DetectorModel implements AutoCloseable{
}
Image img = null;
try {
img = ImageFactory.getInstance().fromFile(Paths.get(imagePath));
DetectedObjects detectedObjects = detect(img);
img = SmartImageFactory.getInstance().fromFile(Paths.get(imagePath));
DetectedObjects detectedObjects = detectCore(img);
if(Objects.isNull(detectedObjects) || detectedObjects.getNumberOfObjects() == 0){
throw new DetectionException("未检测到图片中的物体");
}
@@ -129,9 +123,7 @@ public class DetectorModel implements AutoCloseable{
} catch (IOException e) {
throw new DetectionException(e);
} finally {
if (img != null){
((Mat)img.getWrappedImage()).release();
}
ImageUtils.releaseOpenCVMat(img);
}
}
@@ -140,6 +132,7 @@ public class DetectorModel implements AutoCloseable{
* @param imageData
* @return
*/
@Deprecated
public DetectionResponse detect(byte[] imageData){
if(Objects.isNull(imageData)){
throw new DetectionException("图像无效");
@@ -159,21 +152,20 @@ public class DetectorModel implements AutoCloseable{
* @param image
* @return
*/
@Deprecated
public DetectionResponse detect(BufferedImage image){
if(!ImageUtils.isImageValid(image)){
if(!BufferedImageUtils.isImageValid(image)){
throw new DetectionException("图像无效");
}
Image img = null;
try {
img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(image));
DetectedObjects detectedObjects = detect(img);
img = SmartImageFactory.getInstance().fromBufferedImage(image);
DetectedObjects detectedObjects = detectCore(img);
return DetectorUtils.convertToDetectionResponse(detectedObjects, img);
} catch (Exception e) {
throw new DetectionException(e);
} finally {
if (img != null) {
((Mat)img.getWrappedImage()).release();
}
ImageUtils.releaseOpenCVMat(img);
}
}
@@ -183,30 +175,20 @@ public class DetectorModel implements AutoCloseable{
* @param sourceImage
* @return
*/
@Deprecated
public BufferedImage detectAndDraw(BufferedImage sourceImage){
if(!ImageUtils.isImageValid(sourceImage)){
if(!BufferedImageUtils.isImageValid(sourceImage)){
throw new DetectionException("图像无效");
}
Image img = ImageFactory.getInstance().fromImage(OpenCVUtils.image2Mat(sourceImage));
DetectedObjects detectedObjects = detect(img);
Image img = SmartImageFactory.getInstance().fromBufferedImage(sourceImage);
DetectedObjects detectedObjects = detectCore(img);
if(Objects.isNull(detectedObjects) || detectedObjects.getNumberOfObjects() == 0){
throw new DetectionException("未检测到图片中的物体");
}
img.drawBoundingBoxes(detectedObjects);
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// 调用 save 方法将 Image 写入字节流
img.save(outputStream, "png");
// 将字节流转换为 BufferedImage
byte[] imageBytes = outputStream.toByteArray();
return ImageIO.read(new ByteArrayInputStream(imageBytes));
} catch (IOException e) {
throw new DetectionException("导出图片失败", e);
} finally {
if (img != null) {
((Mat)img.getWrappedImage()).release();
}
}
BufferedImage drawnImage = ImageUtils.toBufferedImage(img);
ImageUtils.releaseOpenCVMat(img);
return drawnImage;
}
/**
@@ -214,7 +196,35 @@ public class DetectorModel implements AutoCloseable{
* @param image
* @return
*/
public DetectedObjects detect(Image image){
public DetectionResponse detect(Image image){
DetectedObjects detectedObjects = detectCore(image);
return DetectorUtils.convertToDetectionResponse(detectedObjects, image);
}
/**
* 检测并绘制
* @param image
* @return
*/
public DetectionResponse detectAndDraw(Image image){
DetectedObjects detectedObjects = detectCore(image);
if(Objects.isNull(detectedObjects) || detectedObjects.getNumberOfObjects() == 0){
throw new DetectionException("未检测到图片中的物体");
}
Image img = ImageUtils.copy(image);
DetectionResponse detectionResponse = DetectorUtils.convertToDetectionResponse(detectedObjects, img);
img.drawBoundingBoxes(detectedObjects);
detectionResponse.setDrawnImage(img);
return detectionResponse;
}
/**
* 目标检测
* @param image
* @return
*/
public DetectedObjects detectCore(Image image){
Predictor<Image, DetectedObjects> predictor = null;
try {
predictor = predictorPool.borrowObject();

View File

@@ -58,5 +58,8 @@ public interface PersonDetModel extends AutoCloseable{
throw new UnsupportedOperationException("默认不支持该功能");
}
default void setFromFactory(boolean fromFactory){
throw new UnsupportedOperationException("默认不支持该功能");
}
}

View File

@@ -9,6 +9,7 @@ import ai.djl.modality.cv.output.Mask;
import ai.djl.modality.cv.output.Rectangle;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.UUID;
import cn.smartjavaai.common.cv.SmartImageFactory;
import cn.smartjavaai.common.entity.*;
import cn.smartjavaai.common.enums.VideoSourceType;
import cn.smartjavaai.common.utils.ImageUtils;
@@ -230,7 +231,7 @@ public class StreamDetector implements AutoCloseable{
mat = converterToMat.convert(frame);
if (mat == null) return;
Image image = ImageFactory.getInstance().fromImage(mat);
Image image = SmartImageFactory.getInstance().fromMat(mat);
DetectedObjects detectedObjects = predictor.predict(image);
// log.info("内部检测结果:{}", detectedObjects.toString());
DetectionResponse detectionResponse = DetectorUtils.convertToDetectionResponse(detectedObjects, image);