发票识别日常生活中经常能用到,之前浏览博客发现类似的文章, 但源码只给了一小部分,所以决定自己来实现。

1.原始图片视角变换及裁剪:

现实中,我们拍照不可能像扫描那样端正,图片中的票据总会有这样那样的视角上的歪斜,使用这样的图片直接来识别极易出错,为了提高识别的准确性,我们需要先将图片预处理为扫描样式的正视图,并将不属于票据的图片部分剪切掉。

针对这一过程,参考相关资料本项目采用“整体图片边缘检测”(采用cv2.Canny函数)、“票据轮廓检测”(采用cv2.findContours函数),得到所需处理票据的轮廓,之后确定票据的四个顶点,最后使用cv2.getPerspectiveTransform和cv2.warpPerspective对于原始图片进行透视变换得到相应的票据的正视图。

2.根据已知位置识别相应文字:

作为第一个版本,简单起见,这里采用了提前找到感兴趣的如发票代码、销售方名称、备注等条目的位置,直接在上述正视图图片中截取对应的区域的方式找到相应信息的图片,通过cnocr库对于得到的区域图片进行文字识别。

3.将识别到的信息保存到相应excel文件中:

import cv2
import numpy as np
from cnocr import CnOcr
import pandas as pd
from pandas import DataFrame
import os
#后续生成票据图像时的大小,按照标准增值税发票版式240mmX140mm来设定
height_resize = 1400
width_resize = 2400
# 调整原始图片尺寸
def resizeImg(image, height=height_resize):
h, w = image.shape[:2]
pro = height / h
size = (int(w * pro), int(height))
img = cv2.resize(image, size)
return img
# 边缘检测
def getCanny(image):
# 高斯模糊
binary = cv2.GaussianBlur(image, (3, 3), 2, 2)
# 边缘检测
binary = cv2.Canny(binary, 60, 240, apertureSize=3)
# 膨胀操作,尽量使边缘闭合
kernel = np.ones((3, 3), np.uint8)
binary = cv2.dilate(binary, kernel, iterations=1)
return binary
# 求出面积最大的轮廓
def findMaxContour(image):
# 寻找边缘
contours, _ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# 计算面积
max_area = 0.0
max_contour = []
for contour in contours:
currentArea = cv2.contourArea(contour)
if currentArea > max_area:
max_area = currentArea
max_contour = contour
return max_contour, max_area
# 多边形拟合凸包的四个顶点
def getBoxPoint(contour):
# 多边形拟合凸包
hull = cv2.convexHull(contour)
epsilon = 0.02 * cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(hull, epsilon, True)
approx = approx.reshape((len(approx), 2))
return approx
# 适配原四边形点集
def adapPoint(box, pro):
box_pro = box
if pro != 1.0:
box_pro = box/pro
box_pro = np.trunc(box_pro)
return box_pro
# 四边形顶点排序,[top-left, top-right, bottom-right, bottom-left]
def orderPoints(pts):
rect = np.zeros((4, 2), dtype="float32")
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect
# 计算长宽
def pointDistance(a, b):
return int(np.sqrt(np.sum(np.square(a - b))))
# 透视变换
def warpImage(image, box):
w, h = pointDistance(box[0], box[1]), \
pointDistance(box[1], box[2])
dst_rect = np.array([[0, 0],
[w - 1, 0],
[w - 1, h - 1],
[0, h - 1]], dtype='float32')
M = cv2.getPerspectiveTransform(box, dst_rect)
warped = cv2.warpPerspective(image, M, (w, h))
return warped
# 统合图片预处理
def imagePreProcessing(path):
image = cv2.imread(path)
# 转灰度、降噪
#image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
#image = cv2.GaussianBlur(image, (3,3), 0)
# 边缘检测、寻找轮廓、确定顶点
ratio = height_resize / image.shape[0]
img = resizeImg(image)
binary_img = getCanny(img)
max_contour, max_area = findMaxContour(binary_img)
boxes = getBoxPoint(max_contour)
boxes = adapPoint(boxes, ratio)
boxes = orderPoints(boxes)
# 透视变化
warped = warpImage(image, boxes)
# 调整最终图片大小
height, width = warped.shape[:2]
#size = (int(width*height_resize/height), height_resize)
size = (width_resize, height_resize)
warped = cv2.resize(warped, size, interpolation=cv2.INTER_CUBIC)
return warped
# 截取图片中部分区域图像,测试阶段使用,包括显示与保存图片,实际使用时不使用这个函数,使用下面的正式版函数
def cropImage_test(img, crop_range, filename='Undefined'):
xpos, ypos, width, height = crop_range
crop = img[ypos:ypos+height, xpos:xpos+width]
if filename=='Undefined': #如果未指定文件名,采用坐标来指定文件名
filename = 'crop-'+str(xpos)+'-'+str(ypos)+'-'+str(width)+'-'+str(height)+'.jpg'
cv2.imshow(filename, crop) #展示截取区域图片---测试用
#cv2.imwrite(filename, crop) #imwrite在文件名含有中文时会有乱码,应该采用下方imencode---测试用
# 保存截取区域图片---测试用
cv2.imencode('.jpg', crop)[1].tofile(filename)
return crop
# 截取图片中部分区域图像
def cropImage(img, crop_range):
xpos, ypos, width, height = crop_range
crop = img[ypos:ypos+height, xpos:xpos+width]
return crop
# 从截取图片中识别文字
def cropOCR(crop, ocrType):
if ocrType==0:
text_crop_list = ocr.ocr_for_single_line(crop)
elif ocrType==1:
text_crop_list = ocr_numbers.ocr_for_single_line(crop)
elif ocrType==2:
text_crop_list = ocr_UpperSerial.ocr_for_single_line(crop)
text_crop = ''.join(text_crop_list)
return text_crop
if __name__ == '__main__':
# 实例化不同用途CnOcr对象
ocr = CnOcr(name='') #混合字符
ocr_numbers = CnOcr(name='numbers', cand_alphabet='0123456789') #纯数字
ocr_UpperSerial = CnOcr(name='UpperSerial', cand_alphabet='0123456789ABCDEFGHIJKLMNPQRSTUVWXYZ') #编号,只包括大写字母(没有O)与数字
# 截取图片中部分区域图像-名称
crop_range_list_name = ['发票代码', '发票号码', '开票日期',
'校验码', '销售方名称', '销售方纳税人识别号',
'销售方地址电话', '销售方开户行及账号', '价税合计',
'备注']
# 截取图片中部分区域图像-坐标
crop_range_list_data = [[1870, 40, 380, 38], [1867, 104, 380,38], [1866, 166, 380, 50],
[1867, 230, 450, 50], [421, 1046, 933, 46], [419, 1091, 933, 48],
[420, 1145, 933, 47], [421, 1193, 933, 40], [1892, 976, 414, 48],
[1455, 1045, 325, 38]]
# 截取图片中部分区域图像-使用ocr的类型,0:混合字符,1:纯数字,2:编号
crop_range_list_type = [1, 1, 0,
1, 0, 2,
0, 0, 0,
0]
# 预处理图像
path = 'test.jpg'
warped = imagePreProcessing(path)
# 展示与保存预处理的图片---测试用
#cv2.imshow('warpImage', warped)
cv2.imwrite('result.jpg',warped)
# 处理预处理图像并将结果保存到text_ocr列表中
text_ocr = []
for i in range(len(crop_range_list_data)):
#filename = crop_range_list_name[i]+'.jpg' #测试阶段保存截取图片时使用的文件名,实际使用时不需要
crop = cropImage(warped, crop_range_list_data[i])
crop_text = cropOCR(crop, crop_range_list_type[i])
crop_text = crop_text.replace('o','0') #发票中不会有小写字母o,凡是出现o的都使用0替代
print(crop_range_list_name[i],':',crop_text)
text_ocr.append(crop_text)
# 按年月来保存结果到xlsx文件中,计算文件名
date_temp = text_ocr[2].split('年')
year_num = date_temp[0]
month_num = date_temp[1].split('月')[0]
filename = year_num+'-'+month_num+'.xlsx'
# 如果文件还没建立,新建文件
if not os.path.exists(filename):
dic = {}
for i in range(len(crop_range_list_name)):
dic[crop_range_list_name[i]] = []
df = pd.DataFrame(dic)
df.to_excel(filename, index=False)
data = pd.read_excel(filename)
if not int(text_ocr[1]) in data['发票号码'].values.tolist():
new_line_num = data.shape[0]
data.loc[new_line_num] = text_ocr
DataFrame(data).to_excel(filename, index=False, header=True)
else:
print(path,'is already in',filename,'!')
cv2.waitKey(0)

Python实现增值税发票OCR(带源码)相关推荐

  1. python设计模式pdf_精通Python设计模式 高清晰PDF+源码

    精通Python设计模式讲述了16种基本设计模式,轻松解决软件设计常见问题:借力高效的Python语言,用现实例子展示各模式关键特性. 本书用实际生活中的例子带你了解常用的设计模式,介绍了诸多有关编写 ...

  2. 【Python 笔记】Linux 下源码编译安装 python

    本文记录在 Linux 上源码编译安装 python 的过程. 文章目录 1. 源码编译安装说明 2. 安装 python2.7 3. 安装 python3.6 1. 源码编译安装说明 安装过程比我想 ...

  3. 分享Python采集99个VB源码,总有一款适合您

    分享Python采集99个VB源码,总有一款适合您 Python采集的99个VB源码下载链接:https://pan.baidu.com/s/1Ljs41rWn_WxvGqFWCkmGsA?pwd=1 ...

  4. 分享Python采集77个VB源码,总有一款适合您

    分享Python采集77个VB源码,总有一款适合您 Python采集的77个VB源码下载链接:https://pan.baidu.com/s/15jH2rxm5RXt7g9uIUFxYhA?pwd=2 ...

  5. python影视数据爬虫sqlite源码+论文(完整版和简洁版)

    python影视数据爬虫sqlite源码+论文(完整版和简洁版)-99源码网,程序代做,代写程序代码,代写编程,代写Java编程,代写php编程,计算机专业代做,计算机毕业设计,网站建设,网站开发,程 ...

  6. python超级画板白板程序源码

    wx供重浩:创享日记 对话框发送:python画板 免费获取完整源码源文件+说明文档+可执行文件等 在PyCharm中运行<超级画板>即可进入如图1所示的系统主界面.在该界面中,通过左侧的 ...

  7. Python+HTML随机点名抽奖源码模板

    介绍: 一款不错的Python+HTML随机点名抽奖源码模板,带背景音乐,bootstrap框架自适应手机端,随机数据在others.js里面增加修改. 网盘下载地址: http://kekewl.c ...

  8. MyEclipse使用总结——使用MyEclipse打包带源码的jar包

    平时开发中,我们喜欢将一些类打包成jar包,然后在别的项目中继续使用,不过由于看不到jar包里面的类的源码了,所以也就无法调试,要想调试,那么就只能通过关联源代码的形式,这样或多或少也有一些不方便,今 ...

  9. cvtcolor python opencv_13行代码实现:Python实时视频采集(附源码)

    程序逻辑 Python实时视频采集程序主要流程共分为10个步骤,具体如下图所示: 流程描述: 库文件导入:导入程序依赖的python安装包: 摄像头管理对象创建和初始化:是对opencv VideoC ...

最新文章

  1. C语言可以不用,但是不能不会!
  2. 原创Python图文详解,堪称「最强文档」还有自动化办公、爬虫pdf速下!
  3. 多种分布式文件系统简介
  4. cookie封装调用
  5. 初赛问题求解及选择题数学相关整理
  6. mysql 默认时间_使用Sysbench对滴滴云MySQL进行基准测试
  7. AngularJS学习之旅—AngularJS 模块(十五)
  8. Perl线程开发过程中的经验
  9. 视觉slam学习笔记以及课后习题《第五讲特征点法视觉里程计》
  10. 今日博文视点大咖直播伴你读No.3:数据分析学习之道
  11. 研究WPS Office的LINUX postinst脚本,发现了错误,应该学习柳氏风格
  12. Hadoop(入门)
  13. Tomcat7安装及配置教程
  14. pdf资源有密码怎么办?
  15. weblogic错误页面
  16. STEP文件格式总结
  17. 初学爬虫-qq空间模拟登录
  18. Oracle中表pagesize,Oracle使用pagesize命令
  19. python对行为进行推理_一道有意思推理题,用python来解答
  20. [日常] NOI2019 退役记

热门文章

  1. 麻省理工学院计算机系硕士课程,麻省理工学院计算机专业研究生申请 你听说过《算法导论吗》?...
  2. android 百度地图h5,【分享】H5 地理信息定位+百度地图
  3. uni App+H5 实现人脸识别
  4. 一些罕见的c++关键字
  5. 2021-2025年中国自攻螺丝行业市场供需与战略研究报告
  6. 学c语言要掌握的英语单词,学习C语言需要理解的英语单词
  7. 学完计算机专业英语感受,学习计算机专业英语的心得体会
  8. 如何下载NBA球员投篮信息
  9. HTML 怎么取消文本框的输入时的边框?
  10. 专访淘宝仲明:揭秘阿里运维部的故障响应机制(