Java+Python混合微服务OCR系统设计

系统架构设计

  • 前端:Java应用(可以是Spring Boot微服务)
  • OCR处理:Python程序(使用PaddleOCR/Tesseract等OCR库)
  • 通信方式:Java通过ProcessBuilder调用Python脚本并传递图像数据
ocr-system/
├── java-service/
│   ├── src/
│   │   └── main/
│   │       ├── java/
│   │       │   └── com/
│   │       │       └── example/
│   │       │           ├── OcrApplication.java
│   │       │           ├── controller/
│   │       │           │   └── OcrController.java
│   │       │           └── service/
│   │       │               └── OcrService.java
│   │       └── resources/
│   │           └── application.properties
│   └── pom.xml
└── python-ocr/
    ├── ocr_processor.py
    ├── requirements.txt
    └── test_images/

扩展方案:

  • REST API:将Python OCR服务部署为独立的HTTP服务
  • gRPC:使用gRPC实现更高效的跨语言通信
  • Jython:直接在JVM中运行Python代码(但可能不支持所有Python库)
  • Py4J:Python和Java互调的另一选择

实现步骤

①、Python OCR服务实现ocr_processor.py

# pip install paddleocr opencv-python numpy

import sys
import json
import base64
import numpy as np
import cv2
from paddleocr import PaddleOCR

#初始化OCR模型(可以按需选择语言等参数)
ocr = PaddleOCR(use_angle_cls=True,lang="ch")

def process_image(image_bytes):
	"""处理图像并返回OCR结果"""
	try:
		#将字节数据转换为numpy数组
		nparr = np.frombuffer(image_bytes,np.uint8)
		img = cv2.imdecode(nparr,cv2.IMREAD_COLOR)

		#执行OCR
		result = ocr.ocr(img,cls=True)

		#格式化结果
		formatted = []
		if result and result[0]:
			for line in result[0]:
				if line and len(line) >= 2:
					text = line[1][0]
					confidence = float(line[1][1])
					formatted.append({
						"text":text,
						"confidence":confidence
						"position":line[0]
					})
		return {"success":True,"result":formatted}
		
	except Exception as e:
		return {"success": False,"error":str(e)}

def main():
	"""主函数,从标砖输入读取数据"""
	try:
		#从标准输入读取Base64编码的图像数据
		input_data = sys.stdin.read()
		data = json.loads(input_data)

		# 解码图像
		image_bytes = base64.b64decode(data["image"])

		#处理图像
		result = process_image(image_bytes)
		#输出结果
		print(json.dumps(result))
	except Exception as e:
		print(json.dumps({"success":False,"error":str(e)}))

if __name__ == "__main__":
	main()
		

②、Java服务实现,创建Spring Boot项目并添加OCR服务调用类

@Service
public class OcrService {

    private static final String PYTHON_SCRIPT_PATH = "path/to/ocr_processor.py";
	
	/**
     * 调用Python OCR处理图像
     * @param imageBytes 图像字节数组
     * @return OCR结果
     */
     public OcrResult processImage(byte[] imageBytes) {
        try {
            // 准备输入数据
            String base64Image = Base64.getEncoder().encodeToString(imageBytes);
            String inputJson = String.format("{\"image\": \"%s\"}", base64Image);
            
            // 构建Python命令
            ProcessBuilder pb = new ProcessBuilder("python3", PYTHON_SCRIPT_PATH);
            pb.redirectErrorStream(true);
            
            // 启动进程
            Process process = pb.start();
            
            // 写入输入数据
            try (OutputStream os = process.getOutputStream();
                 PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8))) {
                writer.println(inputJson);
            }
            
            // 读取输出
            String output;
            try (InputStream is = process.getInputStream();
                 BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
                output = reader.lines().collect(Collectors.joining("\n"));
            }
            
            // 等待进程结束
            int exitCode = process.waitFor();
            if (exitCode != 0) {
                throw new RuntimeException("Python script exited with code: " + exitCode);
            }
            
            // 解析结果
            return parseOcrResult(output);
        } catch (IOException | InterruptedException e) {
            throw new RuntimeException("OCR processing failed", e);
        }
    }
    
    private OcrResult parseOcrResult(String jsonOutput) {
        // 这里简化处理,实际应该使用JSON库如Jackson
        if (jsonOutput.contains("\"success\":true")) {
            // 解析成功结果
            return new OcrResult(true, extractTexts(jsonOutput), null);
        } else {
            // 解析错误
            String error = jsonOutput.split("\"error\":\"")[1].split("\"")[0];
            return new OcrResult(false, null, error);
        }
    }
    
    private List<String> extractTexts(String jsonOutput) {
        // 简化处理,实际应该使用JSON库
        String[] parts = jsonOutput.split("\"text\":\"");
        List<String> texts = new java.util.ArrayList<>();
        for (int i = 1; i < parts.length; i++) {
            texts.add(parts[i].split("\"")[0]);
        }
        return texts;
    }
    
    public static class OcrResult {
        private final boolean success;
        private final List<String> texts;
        private final String error;
        
        public OcrResult(boolean success, List<String> texts, String error) {
            this.success = success;
            this.texts = texts;
            this.error = error;
        }
        
        // Getters
        public boolean isSuccess() { return success; }
        public List<String> getTexts() { return texts; }
        public String getError() { return error; }
    }
}
@RestController
@RequestMapping("/api/ocr")
public class OcrController {

    @Autowired
    private OcrService ocrService;
    
    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public OcrService.OcrResult processImage(@RequestParam("file") MultipartFile file) {
        try {
            byte[] imageBytes = file.getBytes();
            return ocrService.processImage(imageBytes);
        } catch (IOException e) {
            return new OcrService.OcrResult(false, null, "Failed to read image: " + e.getMessage());
        }
    }
}

③、构建和部署

Python端

  • 将ocr_processor.py放在合适的位置
  • 确保Python环境已安装所有依赖
  • 测试Python脚本是否可以独立运行

Java端

  • 构建Spring Boot应用
  • 配置PYTHON_SCRIPT_PATH为正确的Python脚本路径
  • 启动Java应用

使用Postman或者curl测试API

curl -X POST -F "file=@test.png" http://localhost:8080/api/ocr

高级优化

  1. 使用更高效的通信方式

上面的实现使用JSON通过标准输入输出通信,对于大图像可能效率不高。可以考虑:

  • 使用临时文件传递图像
  • 使用gRPC或Thrift进行进程间通信
  • 使用Redis或消息队列解耦
  1. 错误处理和日志

增强错误处理和日志记录:

// 在OcrService中添加日志
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

private static final Logger logger = LoggerFactory.getLogger(OcrService.class);

// 在processImage方法中添加日志
logger.debug("Starting OCR process for image (size: {} bytes)", imageBytes.length);
// ...
logger.debug("Python script output: {}", output);
  1. 性能优化

保持Python OCR模型常驻内存(上面的Python脚本已经实现)

实现Java端的连接池管理多个Python进程

添加缓存层避免重复处理相同图像

  1. 使用更好的JSON处理

替换简单的字符串处理为Jackson等JSON库:

// 添加依赖
// Maven: com.fasterxml.jackson.core:jackson-databind

private OcrResult parseOcrResult(String jsonOutput) throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    Map<String, Object> resultMap = mapper.readValue(jsonOutput, new TypeReference<Map<String, Object>>() {});
    
    if (Boolean.TRUE.equals(resultMap.get("success"))) {
        List<Map<String, Object>> ocrResults = (List<Map<String, Object>>) resultMap.get("result");
        List<String> texts = ocrResults.stream()
            .map(r -> (String) r.get("text"))
            .collect(Collectors.toList());
        return new OcrResult(true, texts, null);
    } else {
        return new OcrResult(false, null, (String) resultMap.get("error"));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值