5

基于PaddleOCR实现AI发票识别的Asp.net Core应用

 2 years ago
source link: https://www.cnblogs.com/neozhu/p/15378363.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

用户批量上传需要识别的照片,上传成功后,系统会启动Hangfire后台Job开始调用PaddleOCR服务返回结果,这个过程有点类似微服务的架构模型。

PaddleOCR

PaddleOCR是百度AI团队开源的一个项目,应该是目前所有免费开源OCR项目中识别效果最好的,具体可以通过PaddleOCR了解,如果你没有Python的开发经验,可能在环境部署上会遇到一些问题,但几乎都能找到解决方案。

Demo https://razor.i247365.net/invoices/index

  1. 用户批量上传要识别的文件,由于我的虚拟机性能非常差,所以才能先上传系统后台自动识别
    上传照片
  2. 系统识别完成后会自动通知用户并修改状态,用户预览识别的结果
  • ASP.NET Core
  • Jquery/Javascript
  • EasyUI
  • Python

安装PaddleOCR环境

经测试PaddleOCR可在glibc 2.23上运行,您也可以测试其他glibc版本或安装glic 2.23
PaddleOCR 工作环境

  • PaddlePaddle 2.0.0
  • python3.7
  • glibc 2.23
  • cuDNN 7.6+ (GPU)

建议使用我们提供的docker运行PaddleOCR,有关docker、nvidia-docker使用请参考链接

如您希望使用 mac 或 windows直接运行预测代码,可以从第2步开始执行。

1. (建议)准备docker环境。第一次使用这个镜像,会自动下载该镜像,请耐心等待。

# 切换到工作目录下
cd /home/Projects
# 首次运行需创建一个docker容器,再次运行时不需要运行当前命令
# 创建一个名字为ppocr的docker容器,并将当前目录映射到容器的/paddle目录下

如果您希望在CPU环境下使用docker,使用docker而不是nvidia-docker创建docker
sudo docker run --name ppocr -v $PWD:/paddle --network=host -it paddlepaddle/paddle:latest-dev-cuda10.1-cudnn7-gcc82 /bin/bash

如果使用CUDA10,请运行以下命令创建容器,设置docker容器共享内存shm-size为64G,建议设置32G以上
sudo nvidia-docker run --name ppocr -v $PWD:/paddle --shm-size=64G --network=host -it paddlepaddle/paddle:latest-dev-cuda10.1-cudnn7-gcc82 /bin/bash

您也可以访问[DockerHub](https://hub.docker.com/r/paddlepaddle/paddle/tags/)获取与您机器适配的镜像。

# ctrl+P+Q可退出docker 容器,重新进入docker 容器使用如下命令
sudo docker container exec -it ppocr /bin/bash

2. 安装PaddlePaddle 2.0

pip3 install --upgrade pip

如果您的机器安装的是CUDA9或CUDA10,请运行以下命令安装
python3 -m pip install paddlepaddle-gpu==2.0.0 -i https://mirror.baidu.com/pypi/simple

如果您的机器是CPU,请运行以下命令安装

python3 -m pip install paddlepaddle==2.0.0 -i https://mirror.baidu.com/pypi/simple

更多的版本需求,请参照[安装文档](https://www.paddlepaddle.org.cn/install/quick)中的说明进行操作。

3. 克隆PaddleOCR repo代码

【推荐】git clone https://github.com/PaddlePaddle/PaddleOCR

如果因为网络问题无法pull成功,也可选择使用码云上的托管:

git clone https://gitee.com/paddlepaddle/PaddleOCR

注:码云托管代码可能无法实时同步本github项目更新,存在3~5天延时,请优先使用推荐方式。

4. 安装第三方库

cd PaddleOCR
pip3 install -r requirements.txt

**如果有问题可以留言,我会帮你处理**

## 重点代码分析
httpClient调用PaddleOCR API
开始自动失败重试策略
```js
services.AddHttpClient("ocr", c =>
            {
                c.BaseAddress = new Uri("https://paddleocr.i247365.net/predict/ocr_system");
                c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            })
            .AddTransientHttpErrorPolicy(policy => policy.WaitAndRetryAsync(3, _ => TimeSpan.FromMilliseconds(1000))); ;

 public void Recognition(int id)
        {
            using (var client = _httpClientFactory.CreateClient("ocr"))
            {
                var invoice = _context.Invoices.Find(id);
                var imgfile = Path.Combine(Directory.GetCurrentDirectory(), invoice.AttachmentUrl);
                var bytes = File.ReadAllBytes(imgfile);
                string base64string = Convert.ToBase64String(bytes);
                var response = client.PostAsJsonAsync<dynamic>("", new { images = new string[] { base64string } }).Result;
            }
            Console.WriteLine($"{id}, completed.");
        }

解析发票信息,目前还是使用比较笨的方法,通过正则表达式来匹配需要的字段,比如发票金额,开票日期,发票号码等等,因为这是免费的并没有提供像收费服务那样更智能的匹配,这里我想只要有足够的数据,应该也可以通过自己训练实现更智能的识别。所以我留了Label字段,目的就是先有人工制定好对应的字段栏位,然后通过坐标数据进行训练。

if(response.StatusCode== System.Net.HttpStatusCode.OK)
                {
                    var result = response.Content.ReadAsStringAsync().Result;
                    var ocr_result = JsonSerializer.Deserialize<ocr_result>(result);
                    var ocr_status = "";
                    invoice.Status = "Done";
                    invoice.Result = ocr_result.status;
                    if (ocr_result.status== "000")
                    {
                        foreach(var collection in ocr_result.results)
                        {
                            foreach(var item in collection)
                            {
                                var rawdata = new InvoiceRawData()
                                {
                                     Confidence=item.confidence,
                                     InvoiceId=id,
                                     Text=item.text,
                                     Text_Region= JsonSerializer.Serialize(item.text_region)
                                };
                                if (item.text.Contains("发票号码"))
                                {
                                    var regex = new Regex("\\d*$");
                                    var mc = regex.Match(item.text);
                                    if(mc.Success)
                                    {
                                        invoice.InvoiceNo = mc.Value;
                                    }
                                }
                                if (item.text.Contains("开票日期"))
                                {
                                    var regex = new Regex("\\d{4}年\\d{2}月\\d{2}日");
                                    var mc = regex.Match(item.text);
                                    if (mc.Success)
                                    {
                                        invoice.InvoiceDate = Convert.ToDateTime(mc.Value.Replace("年","/").Replace("月", "/").Replace("日", ""));
                                    }
                                }
                                if (item.text.Contains("%"))
                                {
                                    var regex = new Regex("^\\d*.\\d*");
                                    var mc = regex.Match(item.text);
                                    if (mc.Success)
                                    {
                                        invoice.TaxRate = decimal.Parse(mc.Value);
                                    }
                                }
                                if (item.text.Contains("¥"))
                                {
                                    var regex = new Regex("\\d.\\d*");
                                    var mc = regex.Match(item.text);
                                    if (mc.Success)
                                    {
                                        invoice.Amount = decimal.Parse(mc.Value);
                                    }
                                }
                                _context.InvoiceRawDatas.Add(rawdata);
                            }
                        }
                        ocr_status = ocr_result.status;
                       
                    }
                    _context.SaveChangesAsync(default).Wait();
                    _hubContext.Clients.All.SendAsync(SignalR.OCRTaskCompleted, new { invoiceNo = invoice.InvoiceNo  });



                }

Canvas 画框标注识别结果

 data.map((item,index) => {
                    $('#rawdata_table > tbody').append(`<tr><td>${index + 1}</td><td>${item.Text}</td><td></td></tr>`);
                    var points = JSON.parse(item.Text_Region);
                    ctx.lineWidth = "5";
                    ctx.strokeStyle = "#00ff00";
                    ctx.textAlign = 'left';
                    ctx.textBaseline = 'top';
                    ctx.fillStyle = "#ff0000";
                    ctx.font = "bold 13px verdana, sans-serif ";
                    ctx.fillText(item.Text, points[0][0], points[0][1]-15);
                    ctx.beginPath();
                    ctx.moveTo(points[0][0], points[0][1]);
                    ctx.lineTo(points[1][0], points[1][1]);
                    ctx.lineTo(points[2][0], points[2][1]);
                    ctx.lineTo(points[3][0], points[3][1]);
                    ctx.closePath();
                    ctx.stroke();
                });

是不是很简单,很酷

Give a Star! ⭐

If you like or are using this project please give it a star. Thanks!
RazorPageCleanArchitecture\features\invoice_ocr


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK