Java使用Opencv + spire.ocr 识别表格
最近有做到需要表格图片识别的需求,开始直接用的百度云API,但是要收费,而且需求不算复杂,只要是截图或保存的Excel生成的图片即可,不涉及复杂的表格,有需要的可以了解一下。使用到三方库opencv(视觉库)+ spire.ocr (免费好用的OCR识别文字)
直接看效果:
19行7列的表格,返回数据:
{“msg”:“操作成功”,“code”:200,“data”:[{“word”:“”,“row”:1,“cal”:1},{“word”:“”,“row”:1,“cal”:2},{“word”:“”,“row”:1,“cal”:3},{“word”:“”,“row”:1,“cal”:4},{“word”:“”,“row”:1,“cal”:5},{“word”:“”,“row”:1,“cal”:6},{“word”:“”,“row”:1,“cal”:7},{“word”:“”,“row”:2,“cal”:1},{“word”:“A-T1-S1M2”,“row”:2,“cal”:2},{“word”:“”,“row”:2,“cal”:3},{“word”:“”,“row”:2,“cal”:4},{“word”:“”,“row”:2,“cal”:5},{“word”:“”,“row”:2,“cal”:6},{“word”:“”,“row”:2,“cal”:7},{“word”:“”,“row”:3,“cal”:1},{“word”:“”,“row”:3,“cal”:2},{“word”:“”,“row”:3,“cal”:3},{“word”:“B-T2-S1M1”,“row”:3,“cal”:4},{“word”:“A-T1-S1M2”,“row”:3,“cal”:5},{“word”:“”,“row”:3,“cal”:6},{“word”:“”,“row”:3,“cal”:7},{“word”:“”,“row”:4,“cal”:1},{“word”:“”,“row”:4,“cal”:2},{“word”:“”,“row”:4,“cal”:3},{“word”:“”,“row”:4,“cal”:4},{“word”:“”,“row”:4,“cal”:5},{“word”:“”,“row”:4,“cal”:6},{“word”:“”,“row”:4,“cal”:7},{“word”:“”,“row”:5,“cal”:1},{“word”:“”,“row”:5,“cal”:2},{“word”:“”,“row”:5,“cal”:3},{“word”:“”,“row”:5,“cal”:4},{“word”:“”,“row”:5,“cal”:5},{“word”:“”,“row”:5,“cal”:6},{“word”:“”,“row”:5,“cal”:7},{“word”:“”,“row”:6,“cal”:1},{“word”:“”,“row”:6,“cal”:2},{“word”:“”,“row”:6,“cal”:3},{“word”:“”,“row”:6,“cal”:4},{“word”:“”,“row”:6,“cal”:5},{“word”:“”,“row”:6,“cal”:6},{“word”:“”,“row”:6,“cal”:7},{“word”:“”,“row”:7,“cal”:1},{“word”:“”,“row”:7,“cal”:2},{“word”:“”,“row”:7,“cal”:3},{“word”:“”,“row”:7,“cal”:4},{“word”:“”,“row”:7,“cal”:5},{“word”:“”,“row”:7,“cal”:6},{“word”:“”,“row”:7,“cal”:7},{“word”:“”,“row”:8,“cal”:1},{“word”:“”,“row”:8,“cal”:2},{“word”:“”,“row”:8,“cal”:3},{“word”:“”,“row”:8,“cal”:4},{“word”:“”,“row”:8,“cal”:5},{“word”:“”,“row”:8,“cal”:6},{“word”:“”,“row”:8,“cal”:7},{“word”:“”,“row”:9,“cal”:1},{“word”:“”,“row”:9,“cal”:2},{“word”:“”,“row”:9,“cal”:3},{“word”:“”,“row”:9,“cal”:4},{“word”:“”,“row”:9,“cal”:5},{“word”:“”,“row”:9,“cal”:6},{“word”:“”,“row”:9,“cal”:7},{“word”:“”,“row”:10,“cal”:1},{“word”:“”,“row”:10,“cal”:2},{“word”:“”,“row”:10,“cal”:3},{“word”:“”,“row”:10,“cal”:4},{“word”:“”,“row”:10,“cal”:5},{“word”:“”,“row”:10,“cal”:6},{“word”:“”,“row”:10,“cal”:7},{“word”:“”,“row”:11,“cal”:1},{“word”:“”,“row”:11,“cal”:2},{“word”:“”,“row”:11,“cal”:3},{“word”:“”,“row”:11,“cal”:4},{“word”:“”,“row”:11,“cal”:5},{“word”:“”,“row”:11,“cal”:6},{“word”:“”,“row”:11,“cal”:7},{“word”:“”,“row”:12,“cal”:1},{“word”:“”,“row”:12,“cal”:2},{“word”:“”,“row”:12,“cal”:3},{“word”:“”,“row”:12,“cal”:4},{“word”:“”,“row”:12,“cal”:5},{“word”:“”,“row”:12,“cal”:6},{“word”:“”,“row”:12,“cal”:7},{“word”:“”,“row”:13,“cal”:1},{“word”:“”,“row”:13,“cal”:2},{“word”:“”,“row”:13,“cal”:3},{“word”:“”,“row”:13,“cal”:4},{“word”:“”,“row”:13,“cal”:5},{“word”:“”,“row”:13,“cal”:6},{“word”:“”,“row”:13,“cal”:7},{“word”:“”,“row”:14,“cal”:1},{“word”:“”,“row”:14,“cal”:2},{“word”:“”,“row”:14,“cal”:3},{“word”:“”,“row”:14,“cal”:4},{“word”:“”,“row”:14,“cal”:5},{“word”:“”,“row”:14,“cal”:6},{“word”:“”,“row”:14,“cal”:7},{“word”:“”,“row”:15,“cal”:1},{“word”:“”,“row”:15,“cal”:2},{“word”:“”,“row”:15,“cal”:3},{“word”:“”,“row”:15,“cal”:4},{“word”:“”,“row”:15,“cal”:5},{“word”:“”,“row”:15,“cal”:6},{“word”:“”,“row”:15,“cal”:7},{“word”:“”,“row”:16,“cal”:1},{“word”:“”,“row”:16,“cal”:2},{“word”:“”,“row”:16,“cal”:3},{“word”:“”,“row”:16,“cal”:4},{“word”:“”,“row”:16,“cal”:5},{“word”:“”,“row”:16,“cal”:6},{“word”:“”,“row”:16,“cal”:7},{“word”:“”,“row”:17,“cal”:1},{“word”:“”,“row”:17,“cal”:2},{“word”:“”,“row”:17,“cal”:3},{“word”:“”,“row”:17,“cal”:4},{“word”:“”,“row”:17,“cal”:5},{“word”:“”,“row”:17,“cal”:6},{“word”:“”,“row”:17,“cal”:7},{“word”:“”,“row”:18,“cal”:1},{“word”:“”,“row”:18,“cal”:2},{“word”:“”,“row”:18,“cal”:3},{“word”:“”,“row”:18,“cal”:4},{“word”:“”,“row”:18,“cal”:5},{“word”:“”,“row”:18,“cal”:6},{“word”:“”,“row”:18,“cal”:7},{“word”:“”,“row”:19,“cal”:1},{“word”:“”,“row”:19,“cal”:2},{“word”:“”,“row”:19,“cal”:3},{“word”:“”,“row”:19,“cal”:4},{“word”:“”,“row”:19,“cal”:5},{“word”:“”,“row”:19,“cal”:6},{“word”:“”,“row”:19,“cal”:7}]}
当然无边框的也可以的,但是要注意,最好把数据设定为适合的行高和列宽,居中对齐,从excel中截图可以使用微信来截图,或者用Snipaste这个小工具,好了,让我们现在开始。
集成spire.ocr进行图片文字识别
使用 OCR 技术扫描识别是获取图片上文字的主要方式。Spire.OCR for Java 能够帮助开发者在 Java 项目中快速批量识别并提取图片上的文字,实现高效的文字提取功能。
由于开源的 tesseract 识别效果不是很好,就采用该OCR工具来识别图片,使用起来效果还是感觉不错的。
官方地址:
https://www.e-iceblue.cn/spire_ocr_java/how-to-scan-and-recognize-text-from-images-in-java-projects.html
pom引入Spire.OCR的依赖,我用的最新版本
<!--ocr图片识别文字-->
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.ocr</artifactId>
<version>${spire.ocr.version}</version>
</dependency>
在application.yml中配置相关参数,主要是必要模型的地址,注意需要换成自己的地址,需要相关文件的可以私信,或者在上面的官网地址下载即可
ocr-scanner:
language: 'Chinese'
model-path-linux: '/root/sunpro/ocr'
model-path-windows: 'E:\spireocr'
配置一下扫描类,加入到Spring容器
package com.ruoyi.ocr.config;
import com.spire.ocr.ConfigureOptions;
import com.spire.ocr.OcrException;
import com.spire.ocr.OcrScanner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @ClassName:OcrScannerConfig
* @Author: mjg
* @Date: 2025/2/12 14:06
* @Description: ocr 扫描器配置类
*/
@Configuration
public class OcrScannerConfig {
@Value("${ocr-scanner.model-path-linux}")
public String modelPathLinux;
@Value("${ocr-scanner.model-path-windows}")
public String modelPathWindows;
@Value("${ocr-scanner.language}")
public String language;
@Bean
public OcrScanner ocrScanner() throws OcrException {
OcrScanner scanner = new OcrScanner();
ConfigureOptions configureOptions = new ConfigureOptions();
// 设置许可证密钥(如有需要)
// LicenseProvider.setLicenseKey("your-license-key");
// 创建并配置扫描器
configureOptions.setLanguage(language);
if(System.getProperty("os.name").toLowerCase().contains("windows")) {
configureOptions.setModelPath(modelPathWindows);
}else{
configureOptions.setModelPath(modelPathLinux);
}
scanner.ConfigureDependencies(configureOptions);
return scanner;
}
}
最后编写个Service方法,供Controller接口调用
/**
* Spire OCR 图片文字识别,限于白底黑字才能识别效果好
* @param path 图片路径
**/
@Override
public String ocrWordBySpireString(String path) {
ocrScanner.scan(path);
String scannedText = null;
try {
scannedText = ocrScanner.getText().toString();
} catch (OcrException e) {
LOGGER.error("识别出错:{}",e.getMessage());
return "";
}
if(org.springframework.util.StringUtils.isEmpty(scannedText)){
return "";
}
return scannedText.replaceAll("Evaluation Warning : The version can be used only for evaluation purpose...", "")
.replaceAll("\\r", "")
.replaceAll("\n","");
}
@RestController
@RequestMapping("ocr/v2")
public class Ocr2Controller {
@Resource
private OcrService ocrService;
@PostMapping(value = "/recognize", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public AjaxResult recognizeText(@RequestParam("file") MultipartFile file) throws OcrException {
// 创建临时文件
File tempFile = null;
try {
// 生成临时文件,使用原始文件名和后缀(避免格式问题)
String originalFilename = file.getOriginalFilename();
String suffix = originalFilename != null ? originalFilename.substring(originalFilename.lastIndexOf('.')) : ".tmp";
tempFile = File.createTempFile("spireocr-", suffix)