.NET Framework 4.7 PaddleOCRSharp 识别 身份证 银行卡


    /// <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