BufferedImage.getGraphics();无法执行,也不报错

本文记录了一次项目中遇到的验证码无法刷新的问题排查过程。起初怀疑是包冲突,尝试多种解决方案无效后,通过逐行日志定位到BufferedImage.getGraphics()执行停滞。最终发现是由于Linux环境下Tomcat未正确配置JAVA_OPTS导致,加入-Djava.awt.headless=true参数后问题得以解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

记一次项目的验证码刷新不出来的问题,开始以为架包冲突,试了所有的方法都无济于事,只有一行代码一行代码的打日志,最后发现在BufferedImage.getGraphics()不往下执行了,原因是linux下tomcat的启动文件要设置JAVA_OPTS="-Djava.awt.headless=true"

感谢原博主的分享:https://blog.youkuaiyun.com/JHdouble/article/details/84809867

java.lang.IllegalArgumentException: Rescaling cannot be performed on an indexed image at java.desktop/java.awt.image.RescaleOp.filter(RescaleOp.java:371) private BufferedImage preprocessImage(BufferedImage image) { // 转换索引图像为RGB格式 image = convertToRGB(image); // 1. 增强对比度 RescaleOp rescaleOp = new RescaleOp(1.2f, 0, null); BufferedImage enhanced = rescaleOp.filter(image, null); // 2. 转换为灰度图 BufferedImage gray = new BufferedImage( enhanced.getWidth(), enhanced.getHeight(), BufferedImage.TYPE_BYTE_GRAY ); Graphics gGray = gray.getGraphics(); gGray.drawImage(enhanced, 0, 0, null); gGray.dispose(); // 3. 简化版自适应二值化(高性能) BufferedImage binary = new BufferedImage( gray.getWidth(), gray.getHeight(), BufferedImage.TYPE_BYTE_BINARY ); int avgBrightness = 0; for (int y = 0; y < gray.getHeight(); y += 5) { for (int x = 0; x < gray.getWidth(); x += 5) { int rgb = gray.getRGB(x, y); avgBrightness += (rgb >> 16) & 0xFF; } } avgBrightness = avgBrightness / ((gray.getWidth()/5) * (gray.getHeight()/5)); int threshold = avgBrightness - 15; for (int y = 0; y < gray.getHeight(); y++) { for (int x = 0; x < gray.getWidth(); x++) { int rgb = gray.getRGB(x, y); int r = (rgb >> 16) & 0xFF; int newRgb = (r > threshold) ? 0xFFFFFF : 0x000000; binary.setRGB(x, y, newRgb); } } // 4. 字符分离优化(腐蚀操作) float[] kernel = {0, 1, 0, 1, 1, 1, 0, 1, 0}; ConvolveOp erodeOp = new ConvolveOp(new Kernel(3, 3, kernel)); return erodeOp.filter(binary, null); } private BufferedImage convertToRGB(BufferedImage src) { if (src.getType() == BufferedImage.TYPE_BYTE_INDEXED) { BufferedImage newImage = new BufferedImage( src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_RGB ); Graphics2D g = newImage.createGraphics(); g.drawImage(src, 0, 0, null); g.dispose(); return newImage; } return src; }
07-25
org.springframework.web.multipart.MultipartException: Current request is not a multipart request 报错 @PostMapping(value = "/validate", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) public List<ValidationResult> validateDocument( @RequestPart("file") MultipartFile file, @RequestPart("expectedConfig") JsonNode expectedConfig) { return param.validateImagePrecisely(file, expectedConfig); } 我的接口 private final ObjectMapper objectMapper = new ObjectMapper(); private static final String BASE_URL = "http://10.191.39.243:8000/v1"; private static final String API_KEY = "Luxsan20250701"; private static final String MODEL_NAME = "/models/Qwen2.5-VL-72B-Instruct"; // 主验证方法 public List<ValidationResult> validateImagePrecisely(MultipartFile file, JsonNode expectedConfig) { try { String imageUrl = convertToBase64(file); // 1. 调用大模型API获取OCR结果 String modelResponse =recognizeTextFromImage(imageUrl); // 2. 解析API响应 JsonNode responseNode = objectMapper.readTree(modelResponse); if (!responseNode.path("success").asBoolean()) { String errorMsg = responseNode.path("message").asText(); log.error("OCR API调用失败: {}", errorMsg); return Collections.singletonList( new ValidationResult("SYSTEM", "API_ERROR", "API调用成功", "API调用失败: " + errorMsg, false) ); } // 3. 提取OCR结果 JsonNode actualData = responseNode.path("data").path("ocr_result"); log.info("OCR识别结果: {}", actualData.toPrettyString()); // 4. 执行精准比较 return preciseCompare(actualData, expectedConfig); } catch (Exception e) { log.error("验证过程异常: {}", e.getMessage(), e); return Collections.singletonList( new ValidationResult("SYSTEM", "EXCEPTION", "正常处理", "系统错误: " + e.getMessage(), false) ); } } private String convertToBase64(MultipartFile file) throws IOException { try (InputStream inputStream = file.getInputStream()) { // 使用缓冲流提高读取效率 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); byte[] data = new byte[4096]; int bytesRead; while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) { buffer.write(data, 0, bytesRead); } buffer.flush(); return Base64.getEncoder().encodeToString(buffer.toByteArray()); } } // 精准字段比较 private List<ValidationResult> preciseCompare(JsonNode actualData, JsonNode expectedConfig) { List<ValidationResult> results = new ArrayList<>(); // 遍历所有预期字段 expectedConfig.fields().forEachRemaining(entry -> { String field = entry.getKey(); String expectedValue = entry.getValue().asText(); JsonNode actualNode = actualData.path(field); if (actualNode.isMissingNode()) { results.add(new ValidationResult("FIELD", field, expectedValue, "字段存在", false)); return; } String actualValue = actualNode.asText(); boolean isValid = actualValue.equals(expectedValue); results.add(new ValidationResult( "FIELD", field, expectedValue, actualValue, isValid )); }); return results; } public String recognizeTextFromImage(String imageUrl) throws IOException { URL url = new URL(BASE_URL); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setRequestProperty("Authorization", "Bearer " + API_KEY); connection.setRequestProperty("Content-Type", "application/json"); connection.setDoOutput(true); // 2. 构建请求体 JSONObject requestBody = new JSONObject(); requestBody.put("model", MODEL_NAME); JSONArray messages = new JSONArray(); JSONObject message = new JSONObject(); message.put("role", "user"); JSONArray content = new JSONArray(); // 文本指令 JSONObject textContent = new JSONObject(); textContent.put("type", "text"); textContent.put("text", "提取图片中的所有文本内容,并按照json格式输出"); content.put(textContent); // 图像URL JSONObject imageContent = new JSONObject(); imageContent.put("type", "image_url"); JSONObject imageUrlObj = new JSONObject(); imageUrlObj.put("url", imageUrl); imageContent.put("image_url", imageUrlObj); content.put(imageContent); message.put("content", content); messages.put(message); requestBody.put("messages", messages); // 3. 发送请求 try (OutputStream os = connection.getOutputStream()) { byte[] input = requestBody.toString().getBytes(StandardCharsets.UTF_8); os.write(input, 0, input.length); } // 4. 获取响应 int responseCode = connection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { try (BufferedReader br = new BufferedReader( new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) { StringBuilder response = new StringBuilder(); String responseLine; while ((responseLine = br.readLine()) != null) { response.append(responseLine.trim()); } // 解析响应,提取模型返回的内容 JSONObject jsonResponse = new JSONObject(response.toString()); JSONArray choices = jsonResponse.getJSONArray("choices"); if (choices.length() > 0) { JSONObject firstChoice = choices.getJSONObject(0); JSONObject messageObj = firstChoice.getJSONObject("message"); return messageObj.getString("content"); } else { throw new IOException("未获取到有效响应"); } } } else { // 读取错误流 try (BufferedReader br = new BufferedReader( new InputStreamReader(connection.getErrorStream(), StandardCharsets.UTF_8))) { StringBuilder errorResponse = new StringBuilder(); String line; while ((line = br.readLine()) != null) { errorResponse.append(line); } throw new IOException("HTTP请求失败: " + responseCode + ", 错误信息: " + errorResponse.toString()); } } }我的方法 测试有问题 然而我这个接口和方法没有问题 @PostMapping(value = "/compare", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) public R compare(@RequestPart("File") MultipartFile file, @RequestPart("jsonContent") String jsonContent) { //读取PDF和图片内容 String pdfText = compareService.extractContent(file); //解析JSON配置 JsonNode jsonConfig = null; List<ValidationResult> results = new ArrayList<>(); try { if (compareService.isPipeSeparatedData(jsonContent)) { jsonConfig = compareService.parsePipeSeparatedDataToJson(jsonContent); results = compareService.compareAndValidate(pdfText, jsonConfig); } else { jsonConfig = compareService.parseJson(jsonContent); results = compareService.compareContent(pdfText, jsonConfig); } } catch (Exception e) { return R.fail(MessageUtils.message("failed.convert.json")); } // 返回没有匹配成功的数据 List<ValidationResult> failedResults = new ArrayList<>(); for (ValidationResult result : results) { if (!result.isValid()) { failedResults.add(result); } } return failedResults.isEmpty() ? R.ok("条件符合规范") : R.fail(failedResults); }private final ObjectMapper objectMapper = new ObjectMapper(); private Tesseract tesseract; @Value("${tesseract.datapath}") private String tessDataPath; /** * ocr配置 */ @PostConstruct public void initOcrEngine() { tesseract = new Tesseract(); //语言包路径和支持语言 tesseract.setDatapath(tessDataPath); tesseract.setLanguage("eng+chi_sim"); tesseract.setPageSegMode(6); // 完全自动页面分割模式 tesseract.setOcrEngineMode(2); // LSTM引擎 } static { //OpenCV本地库 System.load("D:\\work\\OpenCV\\opencv\\build\\java\\x64\\opencv_java4120.dll"); } /** * 支持PDF读取文件和图片ocr */ public String extractContent(MultipartFile file) { String contentType = file.getContentType(); String fileName = file.getOriginalFilename().toLowerCase(); log.info("模版识别:" + fileName); if (contentType == null) { return "支持的文件类型: " + contentType; } if (fileName.endsWith(".pdf")) { return readPdfText(file); } return extractImageText(file); } /** * 读取PDF文本内容 * * @param file * @return */ public String readPdfText(MultipartFile file) { try (PDDocument doc = PDDocument.load(file.getInputStream())) { PDFTextStripper stripper = new PDFTextStripper(); String rawText = stripper.getText(doc); System.out.println("pdf内容" + rawText); return rawText.trim(); } catch (Exception e) { return MessageUtils.message("file.read.pdf.error"); } } /** * OCR识别图片内容 */ private String extractImageText(MultipartFile file) { try (InputStream is = file.getInputStream()) { BufferedImage image = ImageIO.read(is); if (image == null) { return MessageUtils.message("Image.parsing.failed"); } // BufferedImage bufferedImage = preprocessImage(image); // String result = tesseract.doOCR(bufferedImage); String result = tesseract.doOCR(image); result = postProcess(result); result = result.replaceAll("\\s+", " ").trim(); System.out.println("图片内容:\n" + result); return result; } catch (Exception e) { e.printStackTrace(); return MessageUtils.message("file.read.picture.error"); } } private BufferedImage preprocessImage(BufferedImage original) { try { //转换为OpenCV Mat格式 Mat mat = bufferedImageToMat(original); //灰度化 Mat gray = new Mat(); Imgproc.cvtColor(mat, gray, Imgproc.COLOR_BGR2GRAY); //降噪处理 Mat blurred = new Mat(); Imgproc.GaussianBlur(gray, blurred, new Size(3, 3), 0); Mat binary = new Mat(); Imgproc.threshold(blurred, binary, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU); //分离粘连字符 Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(1, 3)); Mat dilated = new Mat(); Imgproc.dilate(binary, dilated, kernel); Mat eroded = new Mat(); Imgproc.erode(dilated, eroded, kernel); //转BufferedImage return matToBufferedImage(eroded); } catch (Exception e) { e.printStackTrace(); return original; // 出错时返回原图 } } // BufferedImage转OpenCV Mat private Mat bufferedImageToMat(BufferedImage image) { int type = BufferedImage.TYPE_3BYTE_BGR; if (image.getType() != type) { BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), type); convertedImage.getGraphics().drawImage(image, 0, 0, null); image = convertedImage; } byte[] data = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); Mat mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC3); // 确保数据元素数量是通道数的整数倍 int expectedLength = mat.rows() * mat.cols() * mat.channels(); if (data.length != expectedLength) { byte[] newData = new byte[expectedLength]; System.arraycopy(data, 0, newData, 0, Math.min(data.length, expectedLength)); data = newData; } mat.put(0, 0, data); return mat; } // OpenCV Mat转BufferedImage private BufferedImage matToBufferedImage(Mat mat) { int type = BufferedImage.TYPE_BYTE_GRAY; if (mat.channels() > 1) { type = BufferedImage.TYPE_3BYTE_BGR; } byte[] data = new byte[mat.cols() * mat.rows() * (int) mat.elemSize()]; mat.get(0, 0, data); BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type); image.getRaster().setDataElements(0, 0, mat.cols(), mat.rows(), data); return image; } private String postProcess(String result) { result = result .replace("scHas", "sch-as") // .replace("(oczg spol s.r.0", "(cz) spol s.r.o.") // .replace("zahradou 101", "zahradou 1018") .replace("¢", "c") .replace("(Shanghai)Co", "applecomputertrading(shanghai)coltd1249centuryavenuetower3,pudongnewdistrict,shanghai200122,china"); return result; } /** * 解析json */ public JsonNode parseJson(String jsonContent) throws Exception { return this.objectMapper.readTree(jsonContent); } /** * 比较pdf读取内容和 ocr图片内容 比较校验 * * @param pdfText * @param jsonConfig * @return */ public List<ValidationResult> compareContent(String pdfText, JsonNode jsonConfig) { List<ValidationResult> results = new ArrayList<>(); //HanLP List<String> hanlpText = HanLP.segment(pdfText).stream() .map(term -> term.word.replaceAll("\\s+", "").toLowerCase()) .collect(Collectors.toList()); System.out.println("分词的效果:" + hanlpText); //处理JSON结构对象/数组 JsonNode dataNode = jsonConfig.isArray() && jsonConfig.size() > 0 ? jsonConfig.get(0) : jsonConfig; //遍历JSON dataNode.fields().forEachRemaining(entry -> { String key = entry.getKey(); String value = entry.getValue().asText(); List<String> hanlpJsonText = HanLP.segment(value).stream() .map(term -> term.word.replaceAll("\\s+", "").toLowerCase()) .collect(Collectors.toList()); if (!hanlpJsonText.isEmpty()) { boolean found = isMatch(hanlpText, hanlpJsonText); if (found) { log.info("字段对比成功:{}", hanlpJsonText); } else { log.info("字段对比失败:{}", hanlpJsonText); } results.add(new ValidationResult( "FIELD", key, String.join("", hanlpJsonText), found ? "Found" : "Not Found", found )); } }); return results; } /** * 转为json结构 * * @param inputData * @return * @throws Exception */ public JsonNode parsePipeSeparatedDataToJson(String inputData) throws Exception { Map<String, String> dataMap = parsePipeSeparatedData(inputData); return objectMapper.valueToTree(dataMap); } /** * 解析分隔数据 */ public Map<String, String> parsePipeSeparatedData(String fileCONTENT) { //处理转义的换行符 fileCONTENT = fileCONTENT.replace("\\n", "\n").replaceAll("\\|+\"$", "").trim(); Map<String, String> dataMap = new LinkedHashMap<>(); String[] lines = fileCONTENT.split("\n"); if (lines.length >= 2) { String[] headers = lines[0].split("\\|"); String[] values = lines[1].split("\\|"); int minLength = Math.min(headers.length, values.length); for (int i = 0; i < minLength; i++) { dataMap.put(headers[i], values[i]); } } return dataMap; } public boolean isPipeSeparatedData(String inputData) { return inputData.contains("|"); } /** * 比较和校验数据 */ public List<ValidationResult> compareAndValidate(String fileContent, JsonNode jsonConfig) { List<ValidationResult> results = new ArrayList<>(); // 处理 JSON 结构对象/数组 JsonNode dataNode = jsonConfig.isArray() && jsonConfig.size() > 0 ? jsonConfig.get(0) : jsonConfig; // HanLP文本分词 List<String> hanlpTest = HanLP.segment(fileContent).stream() .map(term -> term.word.replaceAll("\\s+", "").toLowerCase()) .collect(Collectors.toList()); System.out.println("分词的效果:" + hanlpTest); // 遍历JSON dataNode.fields().forEachRemaining(entry -> { String key = entry.getKey(); String value = entry.getValue().asText(); List<String> hanlpJsonTest = HanLP.segment(value).stream() .map(term -> term.word.replaceAll("\\s+", "").toLowerCase()) .collect(Collectors.toList()); if (!hanlpJsonTest.isEmpty()) { boolean found = isMatch(hanlpTest, hanlpJsonTest); if (found) { log.info("字段对比成功:{}", hanlpJsonTest); } else { log.info("字段对比失败:{}", hanlpJsonTest); } results.add(new ValidationResult( "FIELD", key, String.join("", hanlpJsonTest), found ? "Found" : "Not Found", found )); } }); return results; } private boolean isMatch(List<String> test, List<String> valueTokens) { String Joined = String.join("", test); String valueJoined = String.join("", valueTokens); return Joined.contains(valueJoined); }
最新发布
07-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值