【百度大脑CV主题月征稿计划】iOCR财会票据识
让天涯 发布于2019-09 浏览:1497 回复:0
0
收藏
最后编辑于2022-04

一、功能介绍

针对财会报销场景提出的专项解决方案,可对各类财务票据、报销单、银行回单、对账单进行自动分类及结构化识别,并支持用户为固定版式的新票据/单据自定义结构化识别模板及分类器。

二、应用场景

财税报销票据识别

预置常用发票识别模板和分类器,实现财税场景中票据的自动分类和结构化识别,并提供混贴票据识别功能,可对一张A4纸上粘贴的多张不同类型票据进行识别,可应用于企业财税报销、核算场景。同时,预置了四大行+招商银行主流的银行回单和对账单,配合银行单据预置分类器一步完成分类和结构化,降低财务票据录入成本,提升财税核算效率。
三、使用攻略

说明:本文采用C# 语言,开发环境为.Net Core 2.1,采用在线API接口方式实现。
(1)创建模板

模板管理地址:https://ai.baidu.com/iocr/finance#/templatelist

点击票据模板管理地址,选择“票据”或“单据”模块,选择“创建票据模板"或者“创建单据模板”按钮进行模板创建。

各模板又可以选择“预置模板”或者“自定义模板”,本次测试选择“预置模板”.

如若想体验“自定义模板”,可以参考“自定义模板”教程:https://ai.baidu.com/docs#/iOCR-Finance-Step/8dabdb7a

模板创建完成后,就能看到相应的模板名称、模板图片、模板ID等信息。

创建完成,就可以调用相应的接口进行识别了。

(2)调用接口

文档地址:https://ai.baidu.com/docs#/iOCR-Finance-Intro/top

接口描述:

iOCR财会票据识别是 iOCR 针对财会报销场景提出的专项解决方案,预置多种票据、单据模板及分类器,无需制作或训练即可直接使用;并提供混贴票据识别功能,可对粘贴在一张报销单上的多张不同种类发票进行切分识别;同时支持对未预置的固定版式票据、单据定制结构化识别模板和分类器。详情如下:

预置票据/单据模板及分类器: 预置财会场景常用的 7 类报销发票及四大行+招行对账单模板,同时预置对应分类器,无需制作或训练,即可直接调用实现常用发票及银行单据的自动分类及结构化识别
混贴票据识别: 针对粘贴在一张图上的多张不同种类发票可进行自动检测分类及结构化识别,返回票据种类、位置及结构化识别结果
自定义模板/分类器: 针对未预置且版式固定的票据、单据,仅需上传一张模板图片,即可通过框选参照字段和识别区自助制作一个识别模板,并建立图片中文字的 Key-Value 对应关系,实现对相同版式图片的结构化识别;同时,支持对已发布的多个识别模板自定义分类器,一步实现对不同版式图片的自动分类和结构化识别
iOCR财会票据识别针对财会场景进行深度定制,对常用的各类发票及银行单据进行专项优化及整合,同时提供对长尾票据的模板自定义功能,一站式解决财会报销的自动化识别问题,大幅度提高票据、单据的处理效率。如需了解更多,可访问 iOCR财会票据识别产品介绍页。

HTTP方法:POST

请求URL:https://aip.baidubce.com/rest/2.0/solution/v1/iocr/recognise/finance

请求参数:

返回参数:

返回示例:

使用自定义模板及自定义分类器功能时,返回结果可参考iOCR自定义模板文字识别:https://ai.baidu.com/docs#/iOCR-General-API/fdbd2954
detectorId = 0时,使用票据检测分类功能,返回结果如下所示:

{
    "data": {
        "ret": [{
            "ret": [{
                "word_name": "invoice_code",
                "word": "113001851019"
            }, {
                "word_name": "invoice_rate",
                "word": "拾元整"
            }, {
                "word_name": "invoice_number",
                "word": "25018254"
            }],
            "templateSign": "quota_invoice",
            "scores": 0.95463621616364,
            "isStructured": true
        }, {
            "ret": [{
                "word_name": "invoice_code",
                "word": "113001751019"
            }, {
                "word_name": "invoice_rate",
                "word": "拾元整"
            }, {
                "word_name": "invoice_number",
                "word": "25018226"
            }],
            "templateSign": "quota_invoice",
            "scores": 0.95132255554199,
      "isStructured": true
        }, {
            "ret": [{
                "word_name": "invoice_code",
                "word": "113001851019"
            }, {
                "word_name": "invoice_rate",
                "word": "拾元整"
            }, {
                "word_name": "invoice_number",
                "word": "25018273"
            }],
            "templateSign": "quota_invoice",
            "scores": 0.95567744970322,
            "isStructured": true
        }, {
            "ret": [{
                "word_name": "invoice_code",
                "word": "113001851019"
            }, {
                "word_name": "invoice_rate",
                "word": "拾元整"
            }, {
                "word_name": "invoice_number",
                "word": "25018272"
            }],
            "templateSign": "quota_invoice",
            "scores": 0.9886274933815,
            "isStructured": true
        }],
        "logid": "155601882921432"
    },
  "error_code": 0,
    "error_msg": ""
}

(3)源码共享

(3-1)根据 API Key 和 Secret Key 获取 AccessToken

        /// 
        /// 获取百度access_token
        /// 
        /// API Key
        /// Secret Key
        /// 
        public static string GetAccessToken(string clientId, string clientSecret)
        {
            string authHost = "https://aip.baidubce.com/oauth/2.0/token";
            HttpClient client = new HttpClient();
            List> paraList = new List>();
            paraList.Add(new KeyValuePair("grant_type", "client_credentials"));
            paraList.Add(new KeyValuePair("client_id", clientId));
            paraList.Add(new KeyValuePair("client_secret", clientSecret)); 

            HttpResponseMessage response = client.PostAsync(authHost, new FormUrlEncodedContent(paraList)).Result;
            string result = response.Content.ReadAsStringAsync().Result;
            JObject jo = (JObject)JsonConvert.DeserializeObject(result);

            string token = jo["access_token"].ToString();
            return token;
        }

(3-2)调用API接口获取识别结果

(3-2-1)在Startup.cs 文件 的 Configure(IApplicationBuilder app, IHostingEnvironment env) 方法中开启虚拟目录映射功能:

            string webRootPath = HostingEnvironment.WebRootPath;//wwwroot目录

            app.UseStaticFiles(new StaticFileOptions
            {
                FileProvider = new PhysicalFileProvider(
                    Path.Combine(webRootPath, "Uploads", "BaiduAIs")),
                RequestPath = "/BaiduAIs"
            });

(3-2-2) 建立Index.cshtml文件

(3-2-2-1)前台代码:

    由于html代码无法原生显示,只能简单说明一下:

    主要是一个form表单,需要设置属性enctype="multipart/form-data",否则无法上传图片;

    form表单里面有两个控件:

    一个Input:type="file",asp-for="FileUpload" ,上传图片;

    一个Input:type="submit",asp-page-handler="Finance" ,票据识别。

    一个img:src="@Model.curPath",显示需要识别的图片。

    最后显示后台 msg 字符串列表信息,如果需要输出原始Html代码,则需要使用@Html.Raw()函数。

(3-2-2-2) 后台代码:

        [BindProperty]
        public IFormFile FileUpload { get; set; }
        [BindProperty]
        public string ImageUrl { get; set; }
        private readonly IHostingEnvironment HostingEnvironment;
        public List msg = new List();
        public string curPath { get; set; }

        string BaiduAI_OCRPath="Uploads//BaiduAIs//";
        string BaiduAI_OCRUrl="/BaiduAIs/";
        string OCR_API_KEY="你的API KEY";
        string OCR_SECRET_KEY="你的SECRET KEY";


        public async Task OnPostFinanceAsync()
        {
            if (FileUpload is null)
            {
                ModelState.AddModelError(string.Empty, " 请先选择本地图片!");
            }
            if (!ModelState.IsValid)
            {
                return Page();
            }
            msg = new List();

            string webRootPath = HostingEnvironment.WebRootPath;//wwwroot目录
            string fileDir = Path.Combine(webRootPath, BaiduAI_OCRPath);
            string imgName =  await UploadFile(FileUpload, fileDir);

            string fileName = Path.Combine(fileDir, imgName);
            string imgBase64 = GetFileBase64(fileName);
            curPath = Path.Combine(BaiduAI_OCRUrl, imgName);

            DateTime startTime = DateTime.Now;

            string result = GetiOCRJson(imgBase64, OCR_API_KEY, OCR_SECRET_KEY);

            DateTime endTime = DateTime.Now;
            TimeSpan ts = endTime - startTime;

            JObject jo = (JObject)JsonConvert.DeserializeObject(result);

            try
            {
                msg.Add("iOCR财会票据识别结果(耗时" + ts.TotalSeconds + "秒):\n");

                List msgList = jo["data"]["ret"].ToList();
                int i = 0;
                foreach (JToken ms in msgList)
                {
                    i++;
                    string isStructured = ms["isStructured"].ToString();
                    string templateSign = ms["templateSign"].ToString();
                    List vList = new List();
                    if (isStructured.Trim().ToLower().Equals("true"))
                    {
                        vList = ms["ret"].ToList();
                    }


                    if (msgList.Count > 1)
                    {
                        msg.Add("第 " + i + " 条:\n");
                    }

                    msg.Add("发票类别:" + ms["templateName"].ToString());
                    msg.Add("置信度:" + ms["scores"].ToString());

                    if (vList.Count > 1)
                    {
                        msg.Add("结构化成功:\n");
                        foreach (JToken str in vList)
                        {
                            msg.Add("" + str["word_name"].ToString() + ":" + str["word"].ToString() + "\n");
                        }
                    }
                    else
                    {
                        msg.Add("结构化失败:" + ms["receiptCoordinate"].ToString());
                    }
                }
            }
            catch (Exception e)
            {
                msg.Add(result);
            }
            return Page();
        }

        /// 
        /// 上传文件,返回文件名
        /// 
        /// 文件上传控件
        /// 文件绝对路径
        /// 
        public static async Task UploadFile(IFormFile formFile, string fileDir)
        {
            if (!Directory.Exists(fileDir))
            {
                Directory.CreateDirectory(fileDir);
            }
            string extension = Path.GetExtension(formFile.FileName);
            string imgName = Guid.NewGuid().ToString("N") + extension;
            var filePath = Path.Combine(fileDir, imgName);

            using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
            {
                await formFile.CopyToAsync(fileStream);
            }

            return imgName;
        }

        /// 
        /// 返回图片的base64编码
        /// 
        /// 文件绝对路径名称
        /// 
        public static String GetFileBase64(string fileName)
        {
            FileStream filestream = new FileStream(fileName, FileMode.Open);
            byte[] arr = new byte[filestream.Length];
            filestream.Read(arr, 0, (int)filestream.Length);
            string baser64 =  Convert.ToBase64String(arr);
            filestream.Close();
            return baser64;
        }

        /// 
        /// iOCR财会票据识别Json字符串
        /// 
        /// 图片base64编码
        /// API Key
        /// Secret Key
        /// 自定义模板的ID
        /// 分类器Id,用于指定使用哪个分类器。(0-所有)
        /// 检测器ID,可选值仅有0。(88-所有)
        /// 
        public static string GetiOCRJson( string strbaser64, string clientId, string clientSecret, string templateSign = null, int classifierId = 0, int detectorId = 0)
        {
            string token = GetAccessToken(clientId, clientSecret);
            string host = "https://aip.baidubce.com/rest/2.0/solution/v1/iocr/recognise/finance?access_token=" + token;
            Encoding encoding = Encoding.Default;
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(host);
            request.Method = "post";
            request.ContentType = "application/x-www-form-urlencoded";
            request.KeepAlive = true;
            string str = "image=" + HttpUtility.UrlEncode(strbaser64);
            if (!string.IsNullOrEmpty(templateSign))
            {
                str += "&templateSign=" + templateSign;
            }
            if (!classifierId.Equals(0))
            {
                str += "&classifierId=" + classifierId;
            }
            if (!detectorId.Equals(88))
            {
                str += "&detectorId=" + detectorId;
            }
            byte[] buffer = encoding.GetBytes(str);
            request.ContentLength = buffer.Length;
            request.GetRequestStream().Write(buffer, 0, buffer.Length);
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.Default);
            string result = reader.ReadToEnd();
            return result;
        }

四、效果测试

1、页面:

2、识别结果:

2.1

2.2

2.3

2.4

2.5

2.6

2.7

2.8

2.9

2.10

2.11

五、测试结果及建议

从测试结果可知,iOCR财会票据识别的识别准确率总体上还是可以的,预置的7大类票据模板,基本上可以满足一般的票据识别了,还能同时进行多种票据类型一起识别。如果遇到特殊需求,可以采取“创建票据模板”的方法来进行自定义识别,这样识别效果会好很多。

不过,目前iOCR财会票据默认识别也还存在一些不足

1、识别速度还不快,需要进一步优化。

2、识别结果还不是很准确:

比如第三张火车票识别,金额为123.00元,结果给识别成了12300元;

多张一起识别(第四张)时,有时可能只认出其中部分票据;

或者一张票据(第八张),被认成多张。

当然,这有可能跟被识别的票据的完整性、清晰度等有关,也可能跟票据是否跟默认模板图片一致有关系,所以,一方面,iOCR财会票据识别技术还需要提供更多的通用格式的模板,并优化识别技术,能够降低对识别图片的质量要求,同时提高识别的准确率。另一方面,使用的时候,如果默认识别无法满足识别要求,可以尝试使用自定义模板来进行识别,这样识别结果会准确很多。

3、识别返回结果字段不统一:

识别的票据金额显示方式各异:机打发票是纯数字,名称为TotalTax(第一张);火车票是金额符号+数字,名称为ticket_rates(第二张);行程单是纯数字,名称是ticket_rates(第五张);定额发票是大写数字汉字,名称是invoice_rate(第七张);出租车发票是金额符合+数字,名称是Fare(第九张);

对于票据识别而言,票据面值应该是相当重要的一个字段,所以,最好能将票据的面值识别为统一显示形式,如纯数字,这样的话,方便进行统计计算;此外,对于识别结果的命名,如若能够找出不同票据的通用特点(比如票据抬头,面值等),然后对通用字段进行统一命名,这样就能大大方便进行数据的处理了。

iOCR财会票据识别最重要的应用场景应该就是企业进行财税报销、核算了。不过,企业的财税是一个比较敏感的事情,处理识别结果要准确外,还需要采取一定的措施去保证数据的安全性、保密性。因此,如果想将iOCR财会票据识别功能推广出去,特别是推广到中小企业,还需要制定相应的安全、保密制度,来打消中小企业的顾虑。

收藏
点赞
0
个赞
TOP
切换版块