mirror of
https://github.com/geekwenjie/SmartJavaAI.git
synced 2026-09-14 05:38:50 +00:00
1、OCR:新增表格识别模型
2、OCR:新增9个通用模型 3、OCR:支持批量检测识别 4、OCR:新增更多参数,使用更加灵活 5、人脸识别:支持ID查询及分页获取人脸信息 6、活体检测:视频检测支持设置最大帧数
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 批量数据打包方式:stack,padding
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -23,4 +23,5 @@ public class DetectionRectangle {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package cn.smartjavaai.common.entity.ocr;
|
||||
|
||||
/**
|
||||
* 表格结构
|
||||
* @author dwj
|
||||
*/
|
||||
public class TableStructure {
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user