Java与Python进程通信优化方案

你现在的实现方式每次调用recognizeSliderGap都会创建一个新的Python进程,这确实会有性能开销。我们可以通过进程池或守护进程的方式优化。这里有几种解决方案:

方案1:使用进程池(推荐)

java
package reyo.sdk.utils.opencv.slidecaptcha;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import com.google.gson.Gson;

public class SlideDistanceCalculatorPy {
    
    private static final ExecutorService executor = Executors.newFixedThreadPool(1);
    private static Process pythonProcess = null;
    private static BufferedReader reader = null;
    private static PrintWriter writer = null;
    private static final Gson gson = new Gson();
    
    public static class RecognitionResult {
        private boolean success;
        private int center_x;
        private String error;

        public boolean isSuccess() {
            return success;
        }

        public int getCenterX() {
            return center_x;
        }

        public String getError() {
            return error;
        }
    }
    
    /**
     * 初始化Python守护进程
     */
    public static synchronized boolean initPythonProcess() {
        try {
            if (pythonProcess != null && pythonProcess.isAlive()) {
                return true;
            }
            
            String pythonScript = "C:\\logs\\dnn\\slide_offset_json.py";
            ProcessBuilder pb = new ProcessBuilder("python", pythonScript, "--daemon");
            pythonProcess = pb.start();
            
            reader = new BufferedReader(new InputStreamReader(pythonProcess.getInputStream()));
            writer = new PrintWriter(new OutputStreamWriter(pythonProcess.getOutputStream()), true);
            
            // 等待Python脚本准备就绪
            Thread.sleep(1000);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    
    /**
     * 关闭Python进程
     */
    public static synchronized void closePythonProcess() {
        try {
            if (writer != null) {
                writer.println("EXIT");
                writer.flush();
            }
            
            if (pythonProcess != null && pythonProcess.isAlive()) {
                pythonProcess.waitFor(3, TimeUnit.SECONDS);
                pythonProcess.destroy();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pythonProcess = null;
            reader = null;
            writer = null;
        }
    }
    
    /**
     * 向Python进程发送请求并获取响应
     */
    public static RecognitionResult sendRequestToPython(String imagePath) {
        Future<RecognitionResult> future = executor.submit(() -> {
            try {
                synchronized (SlideDistanceCalculatorPy.class) {
                    if (pythonProcess == null || !pythonProcess.isAlive()) {
                        if (!initPythonProcess()) {
                            RecognitionResult result = new RecognitionResult();
                            result.success = false;
                            result.error = "Failed to initialize Python process";
                            return result;
                        }
                    }
                    
                    // 发送图片路径给Python进程
                    writer.println(imagePath);
                    writer.flush();
                    
                    // 读取Python进程的响应
                    String response = reader.readLine();
                    if (response != null) {
                        return gson.fromJson(response, RecognitionResult.class);
                    } else {
                        RecognitionResult result = new RecognitionResult();
                        result.success = false;
                        result.error = "No response from Python process";
                        return result;
                    }
                }
            } catch (Exception e) {
                RecognitionResult result = new RecognitionResult();
                result.success = false;
                result.error = e.getMessage();
                return result;
            }
        });
        
        try {
            return future.get(10, TimeUnit.SECONDS);
        } catch (Exception e) {
            RecognitionResult result = new RecognitionResult();
            result.success = false;
            result.error = e.getMessage();
            return result;
        }
    }
    
    public static RecognitionResult recognizeSliderGap(String imagePath) {
        return sendRequestToPython(imagePath);
    }
    
    public static int getDistance(String imagePath) {
        RecognitionResult result = recognizeSliderGap(imagePath);
        if (result.isSuccess()) {
            return result.getCenterX();
        } else {
            System.err.println("滑块识别失败: " + result.getError());
            return -1;
        }
    }
    
    public static void main(String[] args) {
        try {
            // 初始化Python进程
            if (!initPythonProcess()) {
                System.out.println("初始化Python进程失败");
                return;
            }
            
            // 循环调用示例
            String[] imagePaths = {
                "C:\\logs\\dnn\\01.png",
                "C:\\logs\\dnn\\02.png",
                "C:\\logs\\dnn\\03.png"
            };
            
            for (String imagePath : imagePaths) {
                long startTime = System.currentTimeMillis();
                int distance = getDistance(imagePath);
                long endTime = System.currentTimeMillis();
                
                if (distance != -1) {
                    System.out.println(String.format("图片: %s, 缺口距离: %d, 耗时: %dms", 
                        imagePath, distance, endTime - startTime));
                }
                
                // 模拟间隔
                Thread.sleep(100);
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closePythonProcess();
            executor.shutdown();
        }
    }
}

方案2:修改Python脚本支持守护进程模式

需要修改你的Python脚本,添加守护进程支持:

python
# slide_offset_json_daemon.py
import sys
import json
import cv2
import numpy as np

def recognize_gap(image_path):
    """原有的识别逻辑"""
    # 这里放你原来的识别代码
    # ...
    result = {"success": True, "center_x": 123}  # 示例
    return result

def daemon_mode():
    """守护进程模式"""
    print("Python daemon ready", flush=True)  # 告诉Java进程已就绪
    
    while True:
        try:
            # 从标准输入读取Java发送的图片路径
            line = sys.stdin.readline().strip()
            if not line:
                break
                
            if line == "EXIT":
                break
                
            # 执行识别
            result = recognize_gap(line)
            
            # 将结果JSON格式输出
            print(json.dumps(result), flush=True)
            
        except Exception as e:
            error_result = {"success": False, "error": str(e)}
            print(json.dumps(error_result), flush=True)

if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == "--daemon":
        daemon_mode()
    else:
        # 原有的单次执行模式
        if len(sys.argv) > 1:
            image_path = sys.argv[1]
            result = recognize_gap(image_path)
            print(json.dumps(result))
        else:
            print(json.dumps({"success": False, "error": "No image path provided"}))

方案3:使用HTTP/RPC服务(更灵活)

创建Python HTTP服务:

python
# slide_api.py
from flask import Flask, request, jsonify
import cv2
import numpy as np
import threading

app = Flask(__name__)

def recognize_gap(image_path):
    """原有的识别逻辑"""
    # 这里放你原来的识别代码
    # ...
    return {"success": True, "center_x": 123}

@app.route('/recognize', methods=['POST'])
def recognize():
    try:
        data = request.json
        image_path = data.get('image_path')
        if not image_path:
            return jsonify({"success": False, "error": "No image path"})
        
        result = recognize_gap(image_path)
        return jsonify(result)
    except Exception as e:
        return jsonify({"success": False, "error": str(e)})

def start_server():
    app.run(host='127.0.0.1', port=5000)

if __name__ == '__main__':
    start_server()

Java客户端:

java
// SlideDistanceCalculatorHttp.java
package reyo.sdk.utils.opencv.slidecaptcha;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

import com.google.gson.Gson;
import com.google.gson.JsonObject;

public class SlideDistanceCalculatorHttp {
    
    private static final HttpClient httpClient = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(10))
            .build();
    
    private static final Gson gson = new Gson();
    
    public static int getDistance(String imagePath) {
        try {
            JsonObject requestBody = new JsonObject();
            requestBody.addProperty("image_path", imagePath);
            
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create("http://127.0.0.1:5000/recognize"))
                    .header("Content-Type", "application/json")
                    .POST(HttpRequest.BodyPublishers.ofString(gson.toJson(requestBody)))
                    .timeout(Duration.ofSeconds(15))
                    .build();
            
            HttpResponse<String> response = httpClient.send(
                    request, HttpResponse.BodyHandlers.ofString());
            
            JsonObject result = gson.fromJson(response.body(), JsonObject.class);
            
            if (result.get("success").getAsBoolean()) {
                return result.get("center_x").getAsInt();
            } else {
                System.err.println("识别失败: " + result.get("error").getAsString());
                return -1;
            }
        } catch (Exception e) {
            System.err.println("HTTP请求失败: " + e.getMessage());
            return -1;
        }
    }
    
    public static void main(String[] args) {
        // 循环调用示例
        String[] imagePaths = {"path1.png", "path2.png", "path3.png"};
        for (String path : imagePaths) {
            int distance = getDistance(path);
            System.out.println("图片: " + path + ", 距离: " + distance);
        }
    }
}

总结

方案1(进程池)最适合你的需求

  • 单次初始化Python进程

  • 通过标准输入输出通信

  • 性能最好,没有HTTP开销

  • 适合高频调用

posted @ 2026-01-06 07:05  锐洋智能  阅读(5)  评论(0)    收藏  举报