package com.ruoyi.system.utils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.tencentcloudapi.common.AbstractModel; import com.tencentcloudapi.common.Credential; import com.tencentcloudapi.common.profile.ClientProfile; import com.tencentcloudapi.common.profile.HttpProfile; import com.tencentcloudapi.common.exception.TencentCloudSDKException; import com.tencentcloudapi.ocr.v20181119.OcrClient; import com.tencentcloudapi.ocr.v20181119.models.*; import com.ruoyi.system.config.TencentOCRConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.File; import java.util.*; import java.nio.file.Files; /** * 腾讯云OCR工具类 * 使用腾讯云OCR服务进行文字识别 * 支持通用文字识别、手写体识别等多种识别类型 * * 使用示例: * // 通用文字识别 * JSONObject result = TencentOCRUtil.generalRecognize("path/to/image.jpg"); * * // 手写体识别 * JSONObject result = TencentOCRUtil.handwritingRecognize("path/to/image.jpg"); */ @Component public class TencentOCRUtil { private static final Logger log = LoggerFactory.getLogger(TencentOCRUtil.class); private static TencentOCRConfig staticTencentOcrConfig; @Autowired public void setTencentOcrConfig(TencentOCRConfig tencentOcrConfig) { TencentOCRUtil.staticTencentOcrConfig = tencentOcrConfig; } /** * 获取腾讯云OCR客户端实例 * @return OcrClient客户端实例 * @throws TencentCloudSDKException SDK异常 */ private static OcrClient getClient() throws TencentCloudSDKException { Credential cred = new Credential( staticTencentOcrConfig.getSecretId(), staticTencentOcrConfig.getSecretKey() ); HttpProfile httpProfile = new HttpProfile(); httpProfile.setEndpoint(staticTencentOcrConfig.getEndpoint()); ClientProfile clientProfile = new ClientProfile(); clientProfile.setHttpProfile(httpProfile); return new OcrClient(cred, "", clientProfile); } /** * 通用文字识别(图片路径) * @param imagePath 图片路径 * @return 识别结果 */ public static JSONObject generalRecognize(String imagePath) { try { byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath()); String base64Image = Base64.getEncoder().encodeToString(imageBytes); OcrClient client = getClient(); GeneralBasicOCRRequest req = new GeneralBasicOCRRequest(); req.setImageBase64(base64Image); GeneralBasicOCRResponse resp = client.GeneralBasicOCR(req); log.info("腾讯云OCR通用文字识别成功,图片路径: {}", imagePath); JSONObject result = new JSONObject(); result.put("success", true); result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp))); result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp)))); return result; } catch (Exception e) { log.error("腾讯云OCR通用文字识别失败: {}", e.getMessage(), e); JSONObject errorResult = new JSONObject(); errorResult.put("success", false); errorResult.put("error", e.getMessage()); return errorResult; } } /** * 通用文字识别(文件对象) * @param imageFile 图片文件对象 * @return 识别结果 */ public static JSONObject generalRecognize(File imageFile) { try { byte[] imageBytes = Files.readAllBytes(imageFile.toPath()); String base64Image = Base64.getEncoder().encodeToString(imageBytes); OcrClient client = getClient(); GeneralBasicOCRRequest req = new GeneralBasicOCRRequest(); req.setImageBase64(base64Image); GeneralBasicOCRResponse resp = client.GeneralBasicOCR(req); log.info("腾讯云OCR通用文字识别成功,文件名: {}", imageFile.getName()); JSONObject result = new JSONObject(); result.put("success", true); result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp))); result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp)))); return result; } catch (Exception e) { log.error("腾讯云OCR通用文字识别失败: {}", e.getMessage(), e); JSONObject errorResult = new JSONObject(); errorResult.put("success", false); errorResult.put("error", e.getMessage()); return errorResult; } } /** * 通用文字识别(图片字节数组) * @param imageBytes 图片字节数组 * @return 识别结果 */ public static JSONObject generalRecognize(byte[] imageBytes) { try { String base64Image = Base64.getEncoder().encodeToString(imageBytes); OcrClient client = getClient(); GeneralBasicOCRRequest req = new GeneralBasicOCRRequest(); req.setImageBase64(base64Image); GeneralBasicOCRResponse resp = client.GeneralBasicOCR(req); log.info("腾讯云OCR通用文字识别成功,字节数组长度: {}", imageBytes.length); JSONObject result = new JSONObject(); result.put("success", true); result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp))); result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp)))); return result; } catch (Exception e) { log.error("腾讯云OCR通用文字识别失败: {}", e.getMessage(), e); JSONObject errorResult = new JSONObject(); errorResult.put("success", false); errorResult.put("error", e.getMessage()); return errorResult; } } /** * 高精度文字识别 * @param imagePath 图片路径 * @return 识别结果 */ public static JSONObject accurateRecognize(String imagePath) { try { byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath()); String base64Image = Base64.getEncoder().encodeToString(imageBytes); OcrClient client = getClient(); GeneralAccurateOCRRequest req = new GeneralAccurateOCRRequest(); req.setImageBase64(base64Image); GeneralAccurateOCRResponse resp = client.GeneralAccurateOCR(req); log.info("腾讯云OCR高精度文字识别成功,图片路径: {}", imagePath); JSONObject result = new JSONObject(); result.put("success", true); result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp))); result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp)))); return result; } catch (Exception e) { log.error("腾讯云OCR高精度文字识别失败: {}", e.getMessage(), e); JSONObject errorResult = new JSONObject(); errorResult.put("success", false); errorResult.put("error", e.getMessage()); return errorResult; } } public static JSONObject handwritingRecognize(String imagePath, String[] itemNames) { try { byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath()); String base64Image = Base64.getEncoder().encodeToString(imageBytes); OcrClient client = getClient(); ExtractDocMultiRequest req = new ExtractDocMultiRequest(); req.setImageBase64(base64Image); // {"患者签名(手印)", "签字人身份证号码", "日期", "联系电话", "本人", "签字人与患者关系"} req.setItemNames(itemNames != null ? itemNames : new String[]{"患者姓名", "性别", "年龄", "身份证号", "诊断", "需支付转运费用", "行程", "开始时间", "结束时间", "家属签名"}); req.setOutputLanguage("cn"); req.setReturnFullText(true); req.setItemNamesShowMode(false); ExtractDocMultiResponse resp = client.ExtractDocMulti(req); log.info("腾讯云OCR手写体识别成功,图片路径: {}", imagePath); // 解析响应数据 JSONObject responseData = JSON.parseObject(AbstractModel.toJsonString(resp)); log.info("手写体识别提取到 {} 个字段", responseData.size()); return responseData; } catch (Exception e) { log.error("腾讯云OCR手写体识别失败: {}", e.getMessage(), e); JSONObject errorResult = new JSONObject(); errorResult.put("error", e.getMessage()); return errorResult; } } /** * 手写体识别 * @param imagePath 图片路径 * @param itemNames 需要提取的字段名称数组 * @return 识别结果 Map,key为AutoName的值,value为AutoContent的值 */ public static Map handwritingRecognizeWith(String imagePath, String[] itemNames) { Map resultMap = new HashMap<>(); try { JSONObject responseData = handwritingRecognize(imagePath, itemNames); // 从StructuralList中提取数据 if (responseData.containsKey("StructuralList") && responseData.getJSONArray("StructuralList") != null) { JSONArray structuralList = responseData.getJSONArray("StructuralList"); for (int i = 0; i < structuralList.size(); i++) { JSONObject structural = structuralList.getJSONObject(i); if (structural.containsKey("Groups") && structural.getJSONArray("Groups") != null) { JSONArray groups = structural.getJSONArray("Groups"); for (int j = 0; j < groups.size(); j++) { JSONObject group = groups.getJSONObject(j); if (group.containsKey("Lines") && group.getJSONArray("Lines") != null) { JSONArray lines = group.getJSONArray("Lines"); for (int k = 0; k < lines.size(); k++) { JSONObject line = lines.getJSONObject(k); String autoName = null; String autoContent = null; // 提取AutoName if (line.containsKey("Key") && line.getJSONObject("Key") != null) { JSONObject key = line.getJSONObject("Key"); if (key.containsKey("AutoName")) { autoName = key.getString("AutoName"); } } // 提取AutoContent if (line.containsKey("Value") && line.getJSONObject("Value") != null) { JSONObject value = line.getJSONObject("Value"); if (value.containsKey("AutoContent")) { autoContent = value.getString("AutoContent"); } } // 将键值对放入结果Map if (autoName != null && autoContent != null) { resultMap.put(autoName, autoContent); } } } } } } } //将 WordList List wordListResult = new ArrayList<>(); if (responseData.containsKey("WordList") && responseData.getJSONArray("WordList") != null){ JSONArray wordList = responseData.getJSONArray("WordList"); for (int i = 0; i < wordList.size(); i++) { JSONObject word = wordList.getJSONObject(i); // { // "Coord": { // "LeftBottom": { // "X": 472, // "Y": 1500 // }, // "LeftTop": { // "X": 467, // "Y": 1420 // }, // "RightBottom": { // "X": 636, // "Y": 1490 // }, // "RightTop": { // "X": 631, // "Y": 1410 // } // }, // "DetectedText": "行程:" // } String detectedText = word.getString("DetectedText"); wordListResult.add(detectedText); } } //我们从wordListResult中行程:后面,需要支付转运费用:之间的文字 String content = extractContentFromWordList(wordListResult, "行程:", "需支付转运费用:"); log.info("提取到行程: {}", content); resultMap.put("行程", content); log.info("手写体识别提取到 {} 个字段", resultMap.size()); return resultMap; } catch (Exception e) { log.error("腾讯云OCR手写体识别失败: {}", e.getMessage(), e); resultMap.put("error", e.getMessage()); return resultMap; } } private static String extractContentFromWordList(List wordListResult, String s, String s1) { //提取s和s1之间的内容 //如果word中只有一或-或->,统一处理成-> int startIndex = -1; int endIndex = -1; for (int i = 0; i < wordListResult.size(); i++) { String word = wordListResult.get(i); if (word.contains(s)) { startIndex = i; } if (word.contains(s1)) { endIndex = i; } } if (startIndex == -1 || endIndex == -1 || startIndex >= endIndex) { return ""; } List w=wordListResult.subList(startIndex + 1, endIndex); Boolean findAle=false; List result=new ArrayList<>(); for(String word:w){ if (!findAle && (word.equals("-") || word.equals("->") || word.equals("→") || word.equals("一") || word.equals("=>")) ){ findAle = true; word = word.replace("-", "→") .replace("一", "→") .replace("=>", "→"); } result.add(word); }; return String.join("", result); } /** * 身份证识别 * @param imagePath 图片路径 * @param cardSide 身份证正反面,"FRONT"表示正面,"BACK"表示反面 * @return 识别结果 */ public static JSONObject idCardRecognize(String imagePath, String cardSide) { try { byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath()); String base64Image = Base64.getEncoder().encodeToString(imageBytes); OcrClient client = getClient(); IDCardOCRRequest req = new IDCardOCRRequest(); req.setImageBase64(base64Image); req.setCardSide(cardSide); IDCardOCRResponse resp = client.IDCardOCR(req); log.info("腾讯云OCR身份证识别成功,图片路径: {},方向: {}", imagePath, cardSide); JSONObject result = new JSONObject(); result.put("success", true); result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp))); result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp)))); return result; } catch (Exception e) { log.error("腾讯云OCR身份证识别失败: {}", e.getMessage(), e); JSONObject errorResult = new JSONObject(); errorResult.put("success", false); errorResult.put("error", e.getMessage()); return errorResult; } } /** * 银行卡识别 * @param imagePath 图片路径 * @return 识别结果 */ public static JSONObject bankCardRecognize(String imagePath) { try { byte[] imageBytes = Files.readAllBytes(new File(imagePath).toPath()); String base64Image = Base64.getEncoder().encodeToString(imageBytes); OcrClient client = getClient(); BankCardOCRRequest req = new BankCardOCRRequest(); req.setImageBase64(base64Image); BankCardOCRResponse resp = client.BankCardOCR(req); log.info("腾讯云OCR银行卡识别成功,图片路径: {}", imagePath); JSONObject result = new JSONObject(); result.put("success", true); result.put("data", JSON.parseObject(AbstractModel.toJsonString(resp))); result.put("content", extractContentFromTencentResult(JSON.parseObject(AbstractModel.toJsonString(resp)))); return result; } catch (Exception e) { log.error("腾讯云OCR银行卡识别失败: {}", e.getMessage(), e); JSONObject errorResult = new JSONObject(); errorResult.put("success", false); errorResult.put("error", e.getMessage()); return errorResult; } } /** * 从腾讯云OCR结果中提取纯文本内容 * @param result OCR识别结果 * @return 提取的文本内容 */ private static String extractContentFromTencentResult(JSONObject result) { StringBuilder content = new StringBuilder(); // 处理通用OCR结果 if (result.containsKey("TextDetections") && result.getJSONArray("TextDetections") != null) { JSONArray textDetections = result.getJSONArray("TextDetections"); for (int i = 0; i < textDetections.size(); i++) { JSONObject detection = textDetections.getJSONObject(i); if (detection.containsKey("DetectedText")) { content.append(detection.getString("DetectedText")).append("\n"); } } } // 处理手写体OCR结果 else if (result.containsKey("Items") && result.getJSONArray("Items") != null) { JSONArray items = result.getJSONArray("Items"); for (int i = 0; i < items.size(); i++) { JSONObject item = items.getJSONObject(i); if (item.containsKey("Itemstring")) { content.append(item.getString("Itemstring")).append("\n"); } } } // 处理身份证OCR结果 else if (result.containsKey("Name") || result.containsKey("Sex") || result.containsKey("Nation") || result.containsKey("Birth") || result.containsKey("Address") || result.containsKey("IdNum")) { if (result.containsKey("Name") && result.getString("Name") != null) { content.append("姓名: ").append(result.getString("Name")).append("\n"); } if (result.containsKey("Sex") && result.getString("Sex") != null) { content.append("性别: ").append(result.getString("Sex")).append("\n"); } if (result.containsKey("Nation") && result.getString("Nation") != null) { content.append("民族: ").append(result.getString("Nation")).append("\n"); } if (result.containsKey("Birth") && result.getString("Birth") != null) { content.append("出生: ").append(result.getString("Birth")).append("\n"); } if (result.containsKey("Address") && result.getString("Address") != null) { content.append("地址: ").append(result.getString("Address")).append("\n"); } if (result.containsKey("IdNum") && result.getString("IdNum") != null) { content.append("身份证号: ").append(result.getString("IdNum")).append("\n"); } } // 处理银行卡OCR结果 else if (result.containsKey("CardNo") && result.getString("CardNo") != null) { content.append("银行卡号: ").append(result.getString("CardNo")).append("\n"); } return content.toString().trim(); } /** * 从识别结果中提取目标字段(金额、日期、备注等) * @param ocrResult OCR识别的原始结果 * @return 提取后的目标字段 */ public static Map extractTargetFields(JSONObject ocrResult) { Map extracted = new HashMap<>(); // 校验OCR结果是否有效 if (!ocrResult.containsKey("success") || !ocrResult.getBooleanValue("success")) { extracted.put("error", ocrResult.getString("error")); return extracted; } // 获取识别的文字内容 String content = ocrResult.getString("content"); if (content == null || content.isEmpty()) { extracted.put("error", "OCR识别结果为空"); return extracted; } // 在内容中查找特定关键词 String[] lines = content.split("\n"); for (String line : lines) { line = line.trim(); // 查找金额相关信息 if (line.contains("金额") || line.contains("合计") || line.contains("总计") || line.matches(".*\\d+\\.\\d{2}.*")) { if (!extracted.containsKey("totalAmount")) { extracted.put("totalAmount", line); } } // 查找日期相关信息 if (line.contains("日期") || line.matches(".*\\d{4}[-/年]\\d{1,2}[-/月]\\d{1,2}.*")) { if (!extracted.containsKey("date")) { extracted.put("date", line); } } // 查找备注相关信息 if (line.contains("备注") || line.contains("说明")) { if (!extracted.containsKey("remark")) { extracted.put("remark", line); } } } // 如果没有找到特定字段,返回全文 if (extracted.isEmpty()) { extracted.put("fullText", content); } return extracted; } /** * 手写体识别(使用默认字段) * @param imagePath 图片路径 * @return 识别结果 Map,key为AutoName的值,value为AutoContent的值 */ public static Map handwritingRecognize(String imagePath) { String[] defaultItemNames = {"患者姓名", "性别", "年龄", "身份证号", "诊断", "需支付转运费用", "行程", "开始时间", "结束时间", "家属签名"}; return handwritingRecognizeWith(imagePath, defaultItemNames); } }