
/// <summary> /// PaddleOCRSharp -Version 4.5.0.1 /// </summary> public class NewController : ApiController { private static PaddleOCREngine _ocrEngine; private static readonly object _lockObj = new object(); /// <summary> /// 获取 OCR 引擎(单例模式) /// </summary> private static PaddleOCREngine GetEngine() { if (_ocrEngine == null) { lock (_lockObj) { if (_ocrEngine == null) { string basePath = AppDomain.CurrentDomain.RelativeSearchPath; string modelPath = Path.Combine(basePath, "inference"); OCRModelConfig config = new OCRModelConfig { det_infer = Path.Combine(modelPath, "ch_PP-OCRv4_det_infer"), rec_infer = Path.Combine(modelPath, "ch_PP-OCRv4_rec_infer"), cls_infer = Path.Combine(modelPath, "ch_ppocr_mobile_v2.0_cls_infer"), keys = Path.Combine(modelPath, "ppocr_keys.txt") }; OCRParameter parameter = new OCRParameter { use_gpu = false, cpu_math_library_num_threads = 4, // 以下参数对识别质量影响很大 det_db_thresh = 0.3f, // 检测阈值,默认0.3 det_db_box_thresh = 0.6f, // 检测框阈值,默认0.6 det_db_unclip_ratio = 1.5f, // 扩张比例,默认1.5 use_dilation = true, // 是否膨胀,默认false(建议开启) rec_img_h = 48, // 识别图像高度,v4模型用48 rec_img_w = 320, // 识别图像宽度 max_side_len = 960, // 图像最大边长(身份证建议960-1280) use_angle_cls = true, // 开启方向分类 cls_thresh = 1 // 方向分类阈值 }; _ocrEngine = new PaddleOCREngine(config, parameter); } } } return _ocrEngine; } #region 身份证识别 /// <summary> /// OCR 识别身份证 /// </summary> /// <returns></returns> [System.Web.Http.HttpPost] [System.Web.Http.Route("New/RecognizeIdCardFromFile")] public BaseDataResult RecognizeIdCardFromFile() { BaseDataResult result = new BaseDataResult(); try { var httpRequest = System.Web.HttpContext.Current.Request; // 1. 检查是否有文件上传 if (httpRequest.Files.Count == 0) { result.success = false; result.data = "请上传图片文件"; return result; } var file = httpRequest.Files[0]; // 2. 检查文件是否为空 if (file == null || file.ContentLength == 0) { result.success = false; result.data = "上传的文件内容为空"; return result; } // 3. 读取文件并识别 using (var ms = new MemoryStream()) { file.InputStream.CopyTo(ms); ms.Position = 0; using (Bitmap bitmap = new Bitmap(ms)) { var engine = GetEngine(); OCRResult ocrResult = engine.DetectText(bitmap); if (ocrResult == null || ocrResult.TextBlocks == null || ocrResult.TextBlocks.Count == 0) { result.success = false; result.data = "未识别到文字"; return result; } // 4. 解析身份证信息 var idCardInfo = ParseIdCardInfo(ocrResult.Text); result.success = true; result.data = idCardInfo; return result; } } } catch (Exception ex) { result.success = false; result.data = $"处理异常: {ex.Message}"; return result; } } /// <summary> /// 解析身份证信息 /// </summary> private object ParseIdCardInfo(string text) { string name = ""; string gender = ""; string ethnicity = ""; string birthYear = ""; string birthMonth = ""; string birthDay = ""; string address = ""; string idNumber = ""; if (string.IsNullOrEmpty(text)) return new { name, gender, ethnicity, birthYear, birthMonth, birthDay, address, idNumber }; // 1. 提取姓名(匹配“姓名”后面的2-4个中文字符,遇到非中文字符或“性别”停止) var nameMatch = Regex.Match(text, @"姓名[\s::]*([\u4e00-\u9fa5]{2,4})(?=性别)"); if (nameMatch.Success) name = nameMatch.Groups[1].Value; // 2. 提取性别(匹配“性别”后面的“男”或“女”) var genderMatch = Regex.Match(text, @"性别[\s::]*([男女])"); if (genderMatch.Success) gender = genderMatch.Groups[1].Value; // 3. 提取民族(匹配“民族”后面的中文字符,遇到“出生”或数字停止) var ethnicMatch = Regex.Match(text, @"民族[\s::]*([\u4e00-\u9fa5]{1,20})(?=出生|年|月|日|\s|$)"); if (ethnicMatch.Success) ethnicity = ethnicMatch.Groups[1].Value; // 4. 提取出生日期(格式:YYYY年MM月DD日 或 YYYY-MM-DD) var birthMatch = Regex.Match(text, @"(\d{4})[年\-](\d{1,2})[月\-](\d{1,2})日?"); if (birthMatch.Success) { birthYear = birthMatch.Groups[1].Value; birthMonth = birthMatch.Groups[2].Value.PadLeft(2, '0'); birthDay = birthMatch.Groups[3].Value.PadLeft(2, '0'); } // 5. 提取身份证号(18位,前17位数字,最后一位数字或X) var idMatch = Regex.Match(text, @"[1-9]\d{16}[\dXx]"); if (idMatch.Success) idNumber = idMatch.Value.ToUpper(); // 6. 提取地址(匹配“住址”后面到身份证号之前的内容) var addressMatch = Regex.Match(text, @"住址[\s::]*(.+?)(?=公民身份号码|身份证号|[1-9]\d{16}|$)"); if (addressMatch.Success) { address = addressMatch.Groups[1].Value.Trim(); } // 如果地址没提取到,尝试更宽松的匹配 if (string.IsNullOrEmpty(address)) { addressMatch = Regex.Match(text, @"住址[\s::]*(.+)"); if (addressMatch.Success) address = addressMatch.Groups[1].Value.Trim(); } // 去除末尾噪点(如 Ss、Ssyy、Syyels 等) address = Regex.Replace(address, @"[Ss][a-zA-Z]*$", ""); address = Regex.Replace(address, @"[1I|]$", ""); return new { name, gender, ethnicity, birthYear, birthMonth, birthDay, address, idNumber }; } #endregion #region 银行卡识别 /// <summary> /// 银行卡识别接口 /// </summary> [System.Web.Http.HttpPost] [System.Web.Http.Route("New/RecognizeBankCard")] public BaseDataResult RecognizeBankCard() { BaseDataResult result = new BaseDataResult(); try { var httpRequest = System.Web.HttpContext.Current.Request; // 1. 检查是否有文件上传 if (httpRequest.Files.Count == 0) { result.success = false; result.data = "请上传图片文件"; return result; } var file = httpRequest.Files[0]; // 2. 检查文件是否为空 if (file == null || file.ContentLength == 0) { result.success = false; result.data = "上传的文件内容为空"; return result; } // 3. 读取文件并识别 using (var ms = new MemoryStream()) { file.InputStream.CopyTo(ms); ms.Position = 0; using (Bitmap bitmap = new Bitmap(ms)) { var engine = GetEngine(); OCRResult ocrResult = engine.DetectText(bitmap); if (ocrResult == null || ocrResult.TextBlocks == null || ocrResult.TextBlocks.Count == 0) { result.success = false; result.data = "未识别到文字"; return result; } // 4. 解析银行卡信息 var bankCardInfo = ParseBankCardInfo(ocrResult.Text); result.success = true; result.data = bankCardInfo; return result; } } } catch (Exception ex) { result.success = false; result.data = $"处理异常: {ex.Message}"; return result; } } /// <summary> /// 解析银行卡信息 /// </summary> private object ParseBankCardInfo(string text) { string cardNumber = ""; string cardNumberFormatted = ""; string bankName = ""; string cardType = ""; if (string.IsNullOrEmpty(text)) return new { cardNumber, cardNumberFormatted, bankName, cardType }; // 1. 提取银行名称(从乱码中匹配) var bankNames = new[] { "工商银行", "农业银行", "中国银行", "建设银行", "交通银行", "招商银行", "浦发银行", "中信银行", "兴业银行", "民生银行", "光大银行", "平安银行", "邮储银行", "广发银行", "华夏银行", "北京银行", "上海银行" }; bankName = bankNames.FirstOrDefault(d => text.Contains(d)) ?? ""; // 从 "ICBC" 等英文缩写识别 if (string.IsNullOrEmpty(bankName)) { if (text.Contains("ICBC")) bankName = "中国工商银行"; else if (text.Contains("ABC")) bankName = "中国农业银行"; else if (text.Contains("BOC")) bankName = "中国银行"; else if (text.Contains("CCB")) bankName = "中国建设银行"; else if (text.Contains("CMB")) bankName = "招商银行"; else if (text.Contains("CITIC")) bankName = "中信银行"; else if (text.Contains("CEB")) bankName = "光大银行"; else if (text.Contains("SPDB")) bankName = "浦发银行"; } // 2. 提取卡类型 if (text.Contains("DEBIT") || text.Contains("借记卡") || text.Contains("储蓄卡")) cardType = "借记卡"; else if (text.Contains("CREDIT") || text.Contains("信用卡")) cardType = "信用卡"; // 3. 提取银行卡号(核心) // 去除所有非数字和非字母的字符,保留原始文本 string rawText = text; // 先尝试直接匹配16位或19位纯数字 var cardMatch = Regex.Match(rawText, @"\d{16,19}"); if (cardMatch.Success) { cardNumber = cardMatch.Value; } else { // 如果直接匹配失败,尝试从乱码中提取连续数字 // 银行卡号通常是凸印的,可能会被识别成带空格的数字 var allNumbers = Regex.Matches(rawText, @"\d{4,6}"); if (allNumbers.Count > 0) { // 拼接所有4-6位数字块,组成完整卡号 cardNumber = string.Join("", allNumbers.Cast<Match>().Select(m => m.Value)); // 只取前16或19位 if (cardNumber.Length > 19) cardNumber = cardNumber.Substring(0, 19); } } // 4. 验证卡号长度(16位或19位) if (cardNumber.Length == 16 || cardNumber.Length == 19) { // 格式化银行卡号(每4位加空格) var parts = new List<string>(); for (int i = 0; i < cardNumber.Length; i += 4) { if (i + 4 <= cardNumber.Length) parts.Add(cardNumber.Substring(i, 4)); else parts.Add(cardNumber.Substring(i)); } cardNumberFormatted = string.Join(" ", parts); } else { // 卡号长度不对,可能是识别错误,清空 cardNumber = ""; } // 5. 如果通过文本没提取到卡号,尝试从原始 TextBlocks 中获取 // 这个需要在调用 ParseBankCardInfo 时传入 ocrResult 对象 return new { cardNumber, cardNumberFormatted, bankName, cardType }; } #endregion }
public class BaseDataResult { /// <summary> /// 成功标识 0失败 1成功 /// </summary> public bool success { get; set; } /// <summary> /// 返回消息 /// </summary> public object data { get; set; } }
测试

文章摘自:https://www.cnblogs.com/hkzw/p/20183656
