1、OCR:新增表格识别模型

2、OCR:新增9个通用模型
3、OCR:支持批量检测识别
4、OCR:新增更多参数,使用更加灵活
5、人脸识别:支持ID查询及分页获取人脸信息
6、活体检测:视频检测支持设置最大帧数
This commit is contained in:
dengwenjie
2025-07-18 12:28:06 +08:00
parent 5d1f074de5
commit 6fbca62e5b
77 changed files with 3544 additions and 842 deletions

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>cn.smartjavaai</groupId>
<artifactId>smartjavaai-parent</artifactId>
<version>1.0.19</version>
<version>1.0.20</version>
</parent>
<name>smartjavaai-common</name>

View File

@@ -0,0 +1,56 @@
package cn.smartjavaai.common.config;
import cn.smartjavaai.common.enums.DeviceEnum;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 模型配置
* @author dwj
*/
@Data
public class ModelConfig {
/**
* 设备类型
*/
private DeviceEnum device;
/**
* gpu设备ID 当device为GPU时生效
*/
private int gpuId = 0;
/**
* 批量数据打包方式stackpadding
*/
private String batchifier;
/**
* 个性化配置(按模型类型动态解析)
*/
private ConcurrentHashMap<String, Object> customParams = new ConcurrentHashMap<>();
public <T> T getCustomParam(String key, Class<T> clazz) {
Object value = customParams.get(key);
if (value == null) return null;
return clazz.cast(value);
}
/**
* 添加个性化配置项
*/
public void putCustomParam(String key, Object value) {
if (customParams == null) {
customParams = new ConcurrentHashMap<>();
}
customParams.put(key, value);
}
}

View File

@@ -23,4 +23,5 @@ public class DetectionRectangle {
this.width = width;
this.height = height;
}
}

View File

@@ -0,0 +1,8 @@
package cn.smartjavaai.common.entity.ocr;
/**
* 表格结构
* @author dwj
*/
public class TableStructure {
}

View File

@@ -370,6 +370,104 @@ public class ImageUtils {
g.drawString(text, x + padding, y + ascent);
}
/**
* 计算左上角,右下角坐标 x0,y0,x1,y1
* Get absolute coordinations
*
* @param rect
* @param width
* @param height
* @return
*/
public static int[] rectXYXY(ai.djl.modality.cv.output.Rectangle rect, int width, int height) {
int left = Math.max((int) (width * rect.getX()), 0);
int top = Math.max((int) (height * rect.getY()), 0);
int right = Math.min((int) (width * (rect.getX() + rect.getWidth())), width - 1);
int bottom = Math.min((int) (height * (rect.getY() + rect.getHeight())), height - 1);
return new int[] {left, top, right, bottom};
}
/**
* 列出文件夹下的所有图片文件
* List all image files under the folder
*
* @param folderPath
* @return
*/
public static List<File> listImageFiles(String folderPath) {
File folder = new File(folderPath);
List<File> imageFiles = new ArrayList<>();
if (folder.exists() && folder.isDirectory()) {
File[] files = folder.listFiles();
if (files == null) {
return imageFiles;
}
for (File file : files) {
if (file.isFile()) {
String name = file.getName().toLowerCase();
if (name.endsWith(".jpg") || name.endsWith(".jpeg") ||
name.endsWith(".png") || name.endsWith(".bmp") ||
name.endsWith(".gif") || name.endsWith(".tiff") ||
name.endsWith(".webp")) {
imageFiles.add(file);
}
}
}
}
return imageFiles;
}
/**
* 读取指定目录下所有图片,返回 List<Image>DJL 格式)
*
* @param folderPath 图片文件夹路径
* @return List<Image>
* @throws IOException
*/
public static List<Image> readImagesFromFolder(String folderPath) throws IOException {
File folder = new File(folderPath);
List<Image> imageList = new ArrayList<>();
if (folder.exists() && folder.isDirectory()) {
File[] files = folder.listFiles();
if (files == null) {
return imageList;
}
for (File file : files) {
if (file.isFile()) {
String name = file.getName().toLowerCase();
if (name.endsWith(".jpg") || name.endsWith(".jpeg") ||
name.endsWith(".png") || name.endsWith(".bmp") ||
name.endsWith(".gif") || name.endsWith(".tiff") ||
name.endsWith(".webp")) {
Image img = ImageFactory.getInstance().fromInputStream(Files.newInputStream(file.toPath()));
imageList.add(img);
}
}
}
}
return imageList;
}
/**
* 判断所有图片尺寸是否一致
*
* @param images 图片列表
*/
public static boolean isAllImageSizeEqual(List<Image> images) {
if (images == null || images.isEmpty()) {
return true; // 空集合视为一致
}
int width = images.get(0).getWidth();
int height = images.get(0).getHeight();
for (Image img : images) {
if (img.getWidth() != width || img.getHeight() != height) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,77 @@
package cn.smartjavaai.common.utils;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.global.opencv_core;
import org.bytedeco.opencv.opencv_core.Mat;
/**
* 视频工具类
* @author dwj
* @date 2025/7/17
*/
public class VideoUtils {
/**
* 视频旋转
* @param inputPath 输入视频路径
* @param outputPath 输出视频路径
* @param angle 旋转角度
* @param format 视频格式
* @param videoCodec 视频编码器
* @throws FFmpegFrameRecorder.Exception
* @throws FFmpegFrameGrabber.Exception
*/
public static void rotateVideo(String inputPath, String outputPath, int angle, String format, int videoCodec) throws FFmpegFrameRecorder.Exception, FFmpegFrameGrabber.Exception {
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputPath);
grabber.start();
int inputWidth = grabber.getImageWidth();
int inputHeight = grabber.getImageHeight();
int outputWidth = inputWidth;
int outputHeight = inputHeight;
if (angle == 90 || angle == 270) {
outputWidth = inputHeight;
outputHeight = inputWidth;
}
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputPath,
outputWidth, outputHeight, grabber.getAudioChannels());
recorder.setVideoCodec(videoCodec);
recorder.setFormat(format);
recorder.start();
Frame frame;
OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
while ((frame = grabber.grab()) != null) {
if (frame.image != null) {
Mat mat = converter.convert(frame);
Mat rotated = new Mat();
switch (angle) {
case 90:
opencv_core.transpose(mat, rotated);
opencv_core.flip(rotated, rotated, 1);
break;
case 180:
opencv_core.flip(mat, rotated, -1);
break;
case 270:
opencv_core.transpose(mat, rotated);
opencv_core.flip(rotated, rotated, 0);
break;
default:
rotated = mat.clone();
break;
}
frame = converter.convert(rotated);
recorder.record(frame);
}
}
recorder.stop();
recorder.release();
grabber.stop();
grabber.release();
}
}