- 新增 语音识别模块,集成 OpenAI 开源的 Whisper 和 Vosk

- 修复 质量评估模型的 Bug
- 修复 OCR 模块 recognizeAndDraw 方法的 Bug
- 修复 车牌识别在未检测到车牌时的报错问题
- 优化 OCR 表格识别功能,新增导出方式
This commit is contained in:
dengwenjie
2025-08-11 10:38:50 +08:00
parent 9b6f5ea9a1
commit 166f0f9d20
46 changed files with 1325 additions and 146 deletions

View File

@@ -1,6 +1,7 @@
package smartai.examples.ocr.common;
import ai.djl.modality.cv.Image;
import cn.smartjavaai.common.config.Config;
import cn.smartjavaai.common.entity.DetectionResponse;
import cn.smartjavaai.common.enums.DeviceEnum;
import cn.smartjavaai.common.utils.ImageUtils;
@@ -14,12 +15,14 @@ import cn.smartjavaai.ocr.model.common.detect.OcrCommonDetModel;
import cn.smartjavaai.ocr.utils.OcrUtils;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.junit.BeforeClass;
import org.junit.Test;
import org.opencv.core.Mat;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.List;
@@ -32,9 +35,17 @@ import java.util.List;
public class OcrDetectionDemo {
//设备类型
public static DeviceEnum device = DeviceEnum.CPU;
@BeforeClass
public static void beforeAll() throws IOException {
//修改缓存路径
// Config.setCachePath("/Users/xxx/smartjavaai_cache");
}
/**
* 获取文本检测模型
* @return

View File

@@ -1,5 +1,6 @@
package smartai.examples.ocr.common;
import cn.smartjavaai.common.config.Config;
import cn.smartjavaai.common.enums.DeviceEnum;
import cn.smartjavaai.ocr.config.DirectionModelConfig;
import cn.smartjavaai.ocr.config.OcrDetModelConfig;
@@ -12,8 +13,10 @@ import cn.smartjavaai.ocr.model.common.detect.OcrCommonDetModel;
import cn.smartjavaai.ocr.model.common.direction.OcrDirectionModel;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.IOException;
import java.util.List;
/**
@@ -28,6 +31,12 @@ public class OcrDirectionDetDemo {
//设备类型
public static DeviceEnum device = DeviceEnum.CPU;
@BeforeClass
public static void beforeAll() throws IOException {
//修改缓存路径
// Config.setCachePath("/Users/xxx/smartjavaai_cache");
}
/**
* 获取方向检测模型
* @return

View File

@@ -1,6 +1,10 @@
package smartai.examples.ocr.common;
import ai.djl.modality.cv.Image;
import ai.djl.util.JsonUtils;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.smartjavaai.common.config.Config;
import cn.smartjavaai.common.enums.DeviceEnum;
import cn.smartjavaai.common.utils.ImageUtils;
import cn.smartjavaai.ocr.config.DirectionModelConfig;
@@ -18,9 +22,15 @@ import cn.smartjavaai.ocr.model.common.direction.OcrDirectionModel;
import cn.smartjavaai.ocr.model.common.recognize.OcrCommonRecModel;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
/**
@@ -35,6 +45,13 @@ public class OcrRecognizeDemo {
//设备类型
public static DeviceEnum device = DeviceEnum.CPU;
@BeforeClass
public static void beforeAll() throws IOException {
//修改缓存路径
//Config.setCachePath("/Users/xxx/smartjavaai_cache");
}
/**
* 获取通用识别模型(不带方向矫正)
* @return
@@ -44,7 +61,7 @@ public class OcrRecognizeDemo {
//指定文本识别模型
recModelConfig.setRecModelEnum(CommonRecModelEnum.PP_OCR_V5_MOBILE_REC_MODEL);
//指定识别模型位置,需要更改为自己的模型路径(下载地址请查看文档)
recModelConfig.setRecModelPath("/Users/xxx/Documents/develop/model/ocr/PP-OCRv5_mobile_rec_infer/PP-OCRv5_mobile_rec_infer.onnx");
recModelConfig.setRecModelPath("/Users/wenjie/Documents/develop/model/ocr/PP-OCRv5_mobile_rec_infer/PP-OCRv5_mobile_rec_infer.onnx");
recModelConfig.setDevice(device);
recModelConfig.setTextDetModel(getDetectionModel());
return OcrModelFactory.getInstance().getRecModel(recModelConfig);
@@ -59,7 +76,7 @@ public class OcrRecognizeDemo {
//指定检测模型
config.setModelEnum(CommonDetModelEnum.PP_OCR_V5_MOBILE_DET_MODEL);
//指定模型位置,需要更改为自己的模型路径(下载地址请查看文档)
config.setDetModelPath("/Users/xxx/Documents/develop/model/ocr/PP-OCRv5_mobile_det_infer/PP-OCRv5_mobile_det_infer.onnx");
config.setDetModelPath("/Users/wenjie/Documents/develop/model/ocr/PP-OCRv5_mobile_det_infer/PP-OCRv5_mobile_det_infer.onnx");
config.setDevice(device);
return OcrModelFactory.getInstance().getDetModel(config);
}
@@ -180,6 +197,59 @@ public class OcrRecognizeDemo {
}
}
@Test
public void recognizeAndDraw2(){
try {
OcrCommonRecModel recModel = getRecModel();
int fontSize = 18;
//创建保存路径
Path inputImagePath = Paths.get("src/main/resources/general_ocr_002.png");
Path imageOutputPath = Paths.get("output/ocr_4_recognized.jpg");
BufferedImage image = null;
image = ImageIO.read(new File(inputImagePath.toAbsolutePath().toString()));
BufferedImage resultImage = recModel.recognizeAndDraw(image, fontSize, new OcrRecOptions());
ImageUtils.saveImage(resultImage, imageOutputPath.toAbsolutePath().toString());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 文本识别并绘制结果返回base64
*/
@Test
public void recognizeAndDrawToBase64(){
try {
OcrCommonRecModel recModel = getRecModel();
int fontSize = 18;
//创建保存路径
Path inputImagePath = Paths.get("src/main/resources/general_ocr_002.png");
byte[] imageBytes = FileUtil.readBytes(inputImagePath);
String base64 = recModel.recognizeAndDrawToBase64(imageBytes, fontSize, new OcrRecOptions());
log.info("base64:{}", base64);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 文本识别并绘制结果返回OcrInfo,OcrInfo中包含base64
*/
@Test
public void recognizeAndDraw3(){
try {
OcrCommonRecModel recModel = getRecModel();
int fontSize = 18;
//创建保存路径
Path inputImagePath = Paths.get("src/main/resources/general_ocr_002.png");
byte[] imageBytes = FileUtil.readBytes(inputImagePath);
OcrInfo ocrInfo = recModel.recognizeAndDraw(imageBytes, fontSize, new OcrRecOptions());
log.info("ocrInfo:{}", JsonUtils.toJson(ocrInfo));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 批量识别
* 注意事项:

View File

@@ -1,6 +1,7 @@
package smartai.examples.ocr.plate;
import ai.djl.util.JsonUtils;
import cn.smartjavaai.common.config.Config;
import cn.smartjavaai.common.entity.R;
import cn.smartjavaai.common.enums.DeviceEnum;
import cn.smartjavaai.common.utils.ImageUtils;
@@ -13,9 +14,11 @@ import cn.smartjavaai.ocr.factory.PlateModelFactory;
import cn.smartjavaai.ocr.model.plate.PlateDetModel;
import cn.smartjavaai.ocr.model.plate.PlateRecModel;
import lombok.extern.slf4j.Slf4j;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
@@ -27,6 +30,12 @@ public class PlateRecDemo {
//设备类型
public static DeviceEnum device = DeviceEnum.CPU;
@BeforeClass
public static void beforeAll() throws IOException {
//修改缓存路径
// Config.setCachePath("/Users/xxx/smartjavaai_cache");
}
/**
* 获取车牌检测模型
* @return

View File

@@ -2,6 +2,7 @@ package smartai.examples.ocr.table;
import ai.djl.modality.cv.Image;
import cn.hutool.core.io.FileUtil;
import cn.smartjavaai.common.config.Config;
import cn.smartjavaai.common.entity.R;
import cn.smartjavaai.common.enums.DeviceEnum;
import cn.smartjavaai.common.utils.ImageUtils;
@@ -21,17 +22,21 @@ import cn.smartjavaai.ocr.model.table.TableRecognizer;
import cn.smartjavaai.ocr.model.table.TableStructureModel;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
/**
* OCR 文本识别 示例
* OCR 表格识别 示例
* 模型下载地址https://pan.baidu.com/s/1MLfd73Vjdpnuls9-oqc9uw?pwd=1234 提取码: 1234
* @author dwj
* @date 2025/5/25
@@ -39,6 +44,13 @@ import java.util.List;
@Slf4j
public class TableRecDemo {
@BeforeClass
public static void beforeAll() throws IOException {
//修改缓存路径
// Config.setCachePath("/Users/xxx/smartjavaai_cache");
}
//设备类型
public static DeviceEnum device = DeviceEnum.CPU;
@@ -51,7 +63,7 @@ public class TableRecDemo {
//指定文本识别模型
recModelConfig.setRecModelEnum(CommonRecModelEnum.PP_OCR_V5_MOBILE_REC_MODEL);
//指定识别模型位置,需要更改为自己的模型路径(下载地址请查看文档)
recModelConfig.setRecModelPath("/Users/xxx/Documents/develop/model/ocr/PP-OCRv5_mobile_rec_infer/PP-OCRv5_mobile_rec_infer.onnx");
recModelConfig.setRecModelPath("/Users/wenjie/Documents/develop/model/ocr/PP-OCRv5_mobile_rec_infer/PP-OCRv5_mobile_rec_infer.onnx");
recModelConfig.setDevice(device);
recModelConfig.setTextDetModel(getDetectionModel());
return OcrModelFactory.getInstance().getRecModel(recModelConfig);
@@ -66,7 +78,7 @@ public class TableRecDemo {
//指定检测模型
config.setModelEnum(CommonDetModelEnum.PP_OCR_V5_MOBILE_DET_MODEL);
//指定模型位置,需要更改为自己的模型路径(下载地址请查看文档)
config.setDetModelPath("/Users/xxx/Documents/develop/model/ocr/PP-OCRv5_mobile_det_infer/PP-OCRv5_mobile_det_infer.onnx");
config.setDetModelPath("/Users/wenjie/Documents/develop/model/ocr/PP-OCRv5_mobile_det_infer/PP-OCRv5_mobile_det_infer.onnx");
// config.setDetModelPath("/Users/xxx/Documents/develop/model/ocr/PP-OCRv5_server_det_infer/PP-OCRv5_server_det.onnx");
config.setDevice(device);
return OcrModelFactory.getInstance().getDetModel(config);
@@ -81,7 +93,7 @@ public class TableRecDemo {
//指定行文本方向检测模型
directionModelConfig.setModelEnum(DirectionModelEnum.PP_LCNET_X0_25);
//指定行文本方向检测模型路径,需要更改为自己的模型路径(下载地址请查看文档)
directionModelConfig.setModelPath("/Users/xxx/Documents/develop/model/ocr/PP-LCNet_x0_25_textline_ori_infer/PP-LCNet_x0_25_textline_ori_infer.onnx");
directionModelConfig.setModelPath("/Users/wenjie/Documents/develop/model/ocr/PP-LCNet_x0_25_textline_ori_infer/PP-LCNet_x0_25_textline_ori_infer.onnx");
directionModelConfig.setDevice(device);
return OcrModelFactory.getInstance().getDirectionModel(directionModelConfig);
}
@@ -95,7 +107,7 @@ public class TableRecDemo {
//指定行文本方向检测模型
config.setModelEnum(TableStructureModelEnum.SLANET_PLUS);
//指定行文本方向检测模型路径,需要更改为自己的模型路径(下载地址请查看文档)
config.setModelPath("/Users/xxx/Documents/develop/model/ocr/slanet-plus/slanet-plus.onnx");
config.setModelPath("/Users/wenjie/Documents/develop/model/ocr/slanet-plus/slanet-plus.onnx");
// config.setModelPath("/Users/xxx/Documents/develop/model/ocr/SLANet_infer/SLANet.onnx");
config.setDevice(device);
return TableRecModelFactory.getInstance().getTableStructureModel(config);
@@ -142,4 +154,47 @@ public class TableRecDemo {
}
}
/**
* 表格识别
* 仅支持简单表格
* 流程:表格结构识别 -> 文本检测 -> 文本识别 -> 合成html table
* 注意事项:
* 1、批量检测时模型应统一放在外层 try 中使用,避免重复加载,自动释放资源更安全。
* 2、模型文件需要放在单独文件夹
*/
@Test
public void recognize2(){
try {
TableStructureModel tableStructureModel = getTableStructureModel();
OcrCommonDetModel detModel = getDetectionModel();
OcrCommonRecModel recModel = getRecModel();
OcrDirectionModel directionModel = getDirectionModel();
//创建表格识别器
TableRecognizer tableRecognizer = TableRecognizer.builder()
.withStructureModel(tableStructureModel)
.withTextDetModel(detModel)
// .withDirectionModel(getDirectionModel()) //如果表格中存在旋转的文字,可以使用方向分类模型
.withTextRecModel(recModel).build();
String imagePath = "src/main/resources/table/table_ch1.png";
BufferedImage image = ImageIO.read(new File(Paths.get(imagePath).toAbsolutePath().toString()));
R<TableStructureResult> result = tableRecognizer.recognize(image);
if(result.isSuccess()){
log.info("result: {}", result.getData().getHtml());
//导出html内容到文件
Path outputPath = Paths.get("output/table_ch2_result.html");
FileUtil.writeUtf8String(result.getData().getHtml(), outputPath.toAbsolutePath().toString());
//绘制表格结构
BufferedImage resultImage = tableRecognizer.drawTable(result.getData(), image);
ImageUtils.saveImage(resultImage, "output/table_ch2_result.jpg");
//导出excel如果导出失败可能是因为表格结果识别的结果是错乱的
try (OutputStream out = Files.newOutputStream(Paths.get("output/table_ch2_result2.xls"))) {
tableRecognizer.exportExcel(result.getData().getHtml(), out);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}