2020/6/13 更新:看了评论区,发现一篇有价值的博客:https://blog.csdn.net/kerlomz/article/details/105974823

该博客主要提供了一种高效利用数据集的方法,鉴于作者写得过于晦涩,我这里再整理一下:

核心思想:训练一个只识别验证码中一种颜色文字的模型。
有效手段:将验证码通过颜色转换,就其余颜色都转化为训练所需的颜色。

举例:
如,训练一个只识别红色的字符的模型,这样我们将能得到原验证码、蓝转红、黄转红、黑转红四组数据集。如下图(从左到右依次为:原图、黄转红、黑转红和蓝转红):

这样一张图片就可以变为四张图片,一组数据集就可以变为四组数据集。在预测的时候,只需要获取需要输入的字符的颜色,然后将其转为红色,输入模型,即可得到待输入字符。
代码如下:

import cv2# cv2 中默认读入图像是(B,G,R)
img = cv2.imread("20200306-180829.png")
cv2.imshow("raw", img)# 蓝色转红色
# img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)  # 蓝色R、G值较小,B值独大,B、G交换,接近红色分布# 黑色转红色
# img[:, :, 2] = 255 - img[:, :, 2]  # 黑色三个通道都比较小, 用255-R通道,则R通道独大,其余通道小,接近红色的分布# 黄色转红色
# img = cv2.bitwise_not(img)  # 黄色R、G值较大,B值较小,先取反,则R、G值较小, B值独大,接近蓝色分布
# img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)  # 与蓝色转红色相同cv2.imshow("convert", img)
cv2.waitKey(0)
更新:鉴于验证码数据标注困难,捣鼓出了一种自动生成方法。请移步:python 发票验证码自动生成

拿到一张发票,如何把上面的内容转化为计算机中结构化的数据呢?

直接拿到图片OCR,虽然目前的技术可以识别出内容,但很关键的问题就是,不知道哪条数据是什么。譬如“100”是数量还是单价。例如下面是一张增值税发票。

OCR识别结果如下:

这样的数据没啥意义。

发票左上角有一个二维码,通过扫描可以得到,发票的四要素(发票代码,发票号码,开票日期,校验码)。然后可以去全国增值税发票查验平台,输入四要素,查看发票详细信息。

二维码识别代码,放这里。要安装zxing包。参考:Python生成+识别二维码

import os
from PIL import Image
import zxing  # 导入解析包
import random# 在当前目录生成临时文件,规避java的路径问题
def ocr_qrcode_zxing(filename):img = Image.open(filename)ran = int(random.random() * 100000)  # 设置随机数据的大小img.save('%s%s.png' % (os.path.basename(filename).split('.')[0], ran))zx = zxing.BarCodeReader()  # 调用zxing二维码读取包data = ''zxdata = zx.decode('%s%s.png' % (os.path.basename(filename).split('.')[0], ran))  # 图片解码print(zxdata)# 删除临时文件os.remove('%s%s.png' % (os.path.basename(filename).split('.')[0], ran))return zxdata  # 返回记录的内容if __name__ == '__main__':filename = '3.png'  # 二维码图片# zxing二维码识别ltext = ocr_qrcode_zxing(filename)  # 将图片文件里的信息转码放到ltext里面
官网查验需要验证码,回到本文主题,验证码识别。

1 数据集

全国增值税发票查验平台的验证码主要构成是:数字、字母、汉字的6位组合,以及红、黄、蓝、黑4种颜色,每次会随机要求输入其中某种颜色。没有颜色要求即是黑色。

基于selenium包进行验证码收集和标注。
参考:
selenium 安装与 chromedriver安装
自动化测试 selenium 模块 webdriver使用

通过对网站的分析发现,必须输入发票的四要素才能获得验证码的图片。

get_captcha.py

from selenium import webdriver #安装:pip install selenium
import time
import base64  #安装:pip install base64def labelme(prex,filename):src = browser.find_element_by_id('yzm_img').get_property('src')filename = prex + str(filename)label = "#"while label.endswith("#"):  #修改:要么从末尾backspace,要么输入"#"回车。注意从中间修改无效。看不清直接回车label = input(filename+":")#输入:内容(字母一律小写)/颜色 颜色编码:除黑色为e,其余均为英文单词首字母label = label.split('/')if label == ['']:return Falseif len(label)!=2 or len(label[0])!=6 or len(label[1])!=6:print("输入长度有误,内容颜色均6位,用/隔开")return Falsewith open('labels.txt', 'a', encoding='utf-8') as f:  # 标签保存在同目录下的labels.txtf.write(filename + ":" + str(label) + "\n")img = base64.b64decode(src.split(',')[-1])with open('pic/'+filename + '.png', 'wb') as f:f.write(img)return Trueif __name__ == "__main__":prex = input('输入唯一标示(姓名首字母,例如:fsf):')cnt = int(input('输入中断文件号,第一次为1:'))browser = webdriver.Chrome()browser.get("http://inv-veri.chinatax.gov.cn")browser.find_element_by_css_selector('#fpdm').send_keys("发票代码")browser.find_element_by_css_selector('#fphm').send_keys("发票号码")browser.find_element_by_css_selector('#kprq').send_keys("开票日期")browser.find_element_by_css_selector('#kjje').send_keys("校验码")time.sleep(1)print('修改:要么从末尾backspace,要么输入"#"回车。注意从中间修改无效。看不清直接回车')print('输入:内容(字母一律小写)/颜色(颜色编码:除黑色为e,其余均为英文单词首字母)')'''获取需要输入的颜色,已注释try:color = browser.find_element_by_css_selector('#yzminfo font').textexcept:color = Noneprint(color)'''while True:if labelme(prex,cnt):cnt += 1browser.find_element_by_css_selector('#imgarea a').click()time.sleep(0.5)

2 模型

EndToEnd文本识别网络-CRNN(CNN+GRU/LSTM+CTC)
参考:语音识别:深入理解CTC Loss原理

输入是一张(35,90,3)的图片,分别是高、宽、通道数。(tensorflow里面高是在第一位)。通过一个CNN,这是一个类似VGG的三层卷积池化层。得到了(4,11,128)的特征向量。之后需要把高宽转置一下,让宽在第一位,reshape成(11,4*128),这个特征的含义,相当于把图片按宽度从左到右分成了11条,每一条是一个512维向量,代表这一条的特征。


之后这个(11,512)的特征向量作为第一个双向GRU的输入,一路从左到右输入到 GRU,一路从右到左输入到 GRU,然后将他们输出的结果加起来(sum)。然后输入第二个双向GRU,还是一路正方向,一路反方向,只不过这次直接将它们的输出连起来(contact)。这样得到了一个(11,128)维的向量。这个时候再分两路,一路全连接层输出11个(标签是6个,这时候就得靠ctc来对齐了)字符的概率,另一个全连接层输出11个颜色的概率。最后就是最小化ctc损失了。这里模型整体有两个ctc损失,一个来自颜色,一个来自字符。我这里按照颜色,字符3:7的权重计算最后总的loss(个人觉得颜色比较简单),这里大家可以自行调整。或者可以颜色训练一轮,字符训练两轮。好了,放代码吧。
先放个代码目录:

简单解释一下:
1.datasets,文件夹就是数据集咯,包括训练集train和测试集test。每个文件夹下又分别有输入inputs.npy、
字符标签labels_str.npy和颜色标签labels_color.npy。
2.get_captcha,数据集的获取和标注。由于本文只使用了少量数据集,想要提升识别效果,可以使用这个代码增加数据集。
其中pic存放验证码图片,labels存放文件名对应的标签。
3.model,一个是模型的代码,一个是模型的训练权重(只用了少量数据集7000,数字字母表现效果良好)。
4.captcha_predict.py, 验证码预测,sigin_in的中间文件。
5.color.txt, 所有颜色。
6.string.txt,所有字符(不全,只包含7000数据集中出现的字符)
7.config.py,配置文件。
8.pretreat.py, 图片预处理文件。
9.evaluate.py, 模型评估文件。
10.train.py, 模型训练文件。
11.sign_in.py,运行该文件,自动登录发票平台,查询发票信息。

ctc_model.py

from keras.layers import Conv2D, MaxPooling2D, Dropout, Flatten, Dense, GRU,\Lambda,Permute,TimeDistributed,Bidirectional
from keras.models import Input,Model
from keras import backend as KHeight = 35
Width = 90
rnn_size = 64  # GRU的隐层大小
n_str = 1288  # 字符类别 + 1
n_color = 5  #
n_len = 6
conv_shape = (None, 11, 512)def ctc_lambda_func(args):y_pred, labels, input_length, label_length = argsreturn K.ctc_batch_cost(labels, y_pred, input_length, label_length)def nn_base(input_tensor):conv1_1 = Conv2D(32, 3, activation='relu', padding='same', kernel_initializer='he_normal')(input_tensor)conv1_2 = Conv2D(32, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv1_1)pool1 = MaxPooling2D((2, 2))(conv1_2)conv2_1 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool1)conv2_2 = Conv2D(64, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv2_1)pool2 = MaxPooling2D((2, 2))(conv2_2)conv3_1 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(pool2)conv3_2 = Conv2D(128, 3, activation='relu', padding='same', kernel_initializer='he_normal')(conv3_1)pool3 = MaxPooling2D((2, 2))(conv3_2)m = Permute((2, 1, 3), name='permute')(pool3)   # 高宽转置flt = TimeDistributed(Flatten(), name='timedistrib')(m)  #相当于flatten最后两个维度des = Dense(32)(flt)gru_1 = Bidirectional(GRU(rnn_size, return_sequences=True, kernel_initializer='he_normal'), merge_mode='sum')(des)gru_2 = Bidirectional(GRU(rnn_size, return_sequences=True, kernel_initializer='he_normal'), merge_mode='concat')(gru_1)x = Dropout(0.25)(gru_2)x1 = Dense(n_str, kernel_initializer='he_normal', activation='softmax',name='str_output')(x)x2 = Dense(n_color, kernel_initializer='he_normal', activation='softmax', name='color_output')(x)return x1,x2def ctc_model(input_tensor, return_layer):labels1 = Input(name='the_labels1', shape=[n_len], dtype='float32')input_length = Input(name='input_length1', shape=[1], dtype='int64')label_length = Input(name='label_length1', shape=[1], dtype='int64')loss_out1 = Lambda(ctc_lambda_func, output_shape=(1,),name='ctc1')([return_layer, labels1, input_length, label_length])model = Model(inputs=[input_tensor, labels1, input_length, label_length], outputs=loss_out1)model.compile(loss={'ctc1': lambda y_true, y_pred: y_pred}, optimizer='adadelta')model.summary()return modelinput_tensor = Input(shape=(Height, Width, 3))
return_layers = nn_base(input_tensor)model_str = ctc_model(input_tensor, return_layers[0])
model_color = ctc_model(input_tensor, return_layers[1])
model_all = Model(input_tensor, [return_layers[0], return_layers[1]])
model_all.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

train.py

from model.ctc_model import *
import os
import numpy as np
from keras.utils import generic_utils
from config import Configdef train():train_x = np.load(os.path.join(Config.train_path,"inputs.npy"))train_y_str = np.load(os.path.join(Config.train_path,"labels_str.npy"))train_y_color = np.load(os.path.join(Config.train_path,"labels_color.npy"))best_loss = np.infn_epoch = Config.n_epochbatch_size = Config.batch_sizedata_length = len(train_x)steps = data_length // batch_size + 1losses = np.zeros(shape=(steps, 2))  # 记录每个step的两个loss,一个epoch后又覆盖for epoch in range(n_epoch):bar = generic_utils.Progbar(steps)   # keras进度条print('Epoch {}/{}'.format(epoch + 1, n_epoch))for step in range(steps):start = batch_size * stepend = min(batch_size * (step + 1), data_length)   # 获取批次首尾X = train_x[start:end]Y = train_y_str[start:end]Y1 = train_y_color[start:end]loss_str = model_str.train_on_batch([X, Y, np.array(np.ones(len(X)) * int(conv_shape[1])),np.array(np.ones(len(X), ) * n_len)], Y)loss_color = model_color.train_on_batch([X, Y1, np.array(np.ones(len(X)) * int(conv_shape[1])),np.array(np.ones(len(X), ) * n_len)], Y1)losses[step, 0] = loss_strlosses[step, 1] = loss_colorbar.update(step, [('str', np.mean(losses[:step + 1, 0])), ('color', np.mean(losses[:step + 1, 1]))])mean_loss_str = np.mean(losses[:, 0])mean_loss_color = np.mean(losses[:, 1])mean_all_loss = 0.7 * mean_loss_str + 0.3 * mean_loss_color  # 3:7 分配loss权重print()print("loss_str {} loss_color {} all_loss {}".format(mean_loss_str, mean_loss_color, mean_all_loss))if mean_all_loss < best_loss:  # 保存最佳val_loss的模型best_loss = mean_all_lossprint("save weights")model_all.save_weights(Config.model_path)

运行train.py就可以开始训练啦。

完了之后,模型用起来。

captcha_predict.py

from model.ctc_model import model_all,conv_shape
from pretreat import img_pretreat
from PIL import Image
import numpy as np
from keras import backend as K
from config import Configdef predict(filename):'''根据识别结果返回颜色和字符:param filename: 验证码图片路径:return:'''img = Image.open(filename)arr = np.expand_dims(img_pretreat(img),0)model_all.load_weights(Config.model_path)result = model_all.predict(arr)pred_str = K.get_value(K.ctc_decode(result[0], input_length=np.ones(1, dtype=int) * int(conv_shape[1]), )[0][0])[:, :6]  # ctc解码,[:,:6]只取前6位pred_color = K.get_value(K.ctc_decode(result[1], input_length=np.ones(1, dtype=int) * int(conv_shape[1]), )[0][0])[:, :6]with open(Config.str_table_path,"r",encoding="utf-8") as f:t_string = f.read()with open(Config.color_table_path, "r", encoding="utf-8") as f:t_color = f.read()# print(pred_str,pred_color)strings = [search_table(t_string,item)for item in pred_str[0]]colors = [search_table(t_color,item)for item in pred_color[0]]return strings,colorsdef search_table(table,idx):'''排除掉小于0或者大于分类数目的索引:param table: srting or color:param idx: 索引:return:'''if idx < 0:return "0"elif idx >= len(table):return "1"else:return table[idx]

最后selenium脚本一键登录

sign_in.py

from selenium import webdriver
import time
import base64
from captcha_predict import predictdef captcha_img_ocr(input_color=None):''':param input_color: 要求输入的颜色:return:待输入字符串'''filename = time.strftime("%Y%m%d-%H%M%S")+'.png'src = browser.find_element_by_id('yzm_img').get_property('src')img = base64.b64decode(src.split(',')[-1])with open(filename, 'wb') as f:f.write(img)strings, colors = predict(filename)if input_color:res = ""for char,_color in zip(strings,colors):if _color == input_color:res += charelse:res = "".join(strings)return resdef set_text():try:color = browser.find_element_by_css_selector('#yzminfo font').text[0]except:color = Noneprint(color)text = captcha_img_ocr(color)print(text)browser.find_element_by_css_selector('#yzm').clear()browser.find_element_by_css_selector('#yzm').send_keys(text)if __name__ == "__main__":browser = webdriver.Chrome()browser.get("http://inv-veri.chinatax.gov.cn")browser.find_element_by_css_selector('#fpdm').send_keys("发票代码")browser.find_element_by_css_selector('#fphm').send_keys("发票号码")browser.find_element_by_css_selector('#kprq').send_keys("开票日期")browser.find_element_by_css_selector('#kjje').send_keys("校验码")time.sleep(1)set_text()check = browser.find_element_by_css_selector('#checkfp')check.click()time.sleep(1)try:popup = browser.find_element_by_css_selector('#popup_ok')except:popup = Nonewhile popup:  # 输错后弹窗,点击换验证码,反复输入popup.click()browser.find_element_by_css_selector('#imgarea a').click()time.sleep(1)set_text()check.click()time.sleep(1)try:popup = browser.find_element_by_css_selector('#popup_ok')except:popup = None
源码:https://download.csdn.net/download/okfu_dl/11193913

全国增值税发票查验平台验证码识别相关推荐

  1. 国税总局增值税发票查验平台验证码识别深度学习实战

    国家税务总局全国增值税发票查验平台验证码 查验验证码图片如下面所示: 测试地址:http://47.99.174.98:8808/ 1.验证码识别Python版 import base64 impor ...

  2. 国家税务总局全国增值税发票查验平台验证码刷不出来显示系统繁忙的解决方法

    虽然我们解决了访问https://inv-veri.chinatax.gov.cn/不行的问题,但是验证码的接口依然是不同的网址,需要解决验证码接口的访问问题. 找一下你要查验发票的地址,比如我的发票 ...

  3. 国税总局发票查验平台验证码识别方案,识别率达98%

    全国增值税发票查验平台验证码 2020.04.30 已经同步更新,测试网址不变 手动置顶:验证码识别测试页面(可视化操作) 识别率97.5%,图片接口支持手动测试,以图片形式返回结果:文本接口需要联系 ...

  4. 全国增值税发票查验平台 | 免验证码

    国家税务总局全国增值税发票查验平台 众所周知,发票查验有一个肉眼难以辨认的验证码,今天推荐给各位的是国税总局发票查验的免码接口,目前支持全发票种类,你们看到的没错,是全发票种类,市面上的API接口也不 ...

  5. 国家税务总局全国增值税发票查验平台网站js逆向分析及全逆向算法还原

    本文教程针对的是2021年7月2日时国税查验平台的js分析,其中版本号为V2.0.06_009.主要分析内容为key9和flwq39以及fplx这3个参数的算法,其中key9分为获取验证码阶段和查验阶 ...

  6. 国家税务总局全国增值税发票查验平台-1

    准备做个查验记录保存,方便数据归集.拿python练手. 1 安装控件 pip install pywebview pip install cefpython3 2 功能实现 2.1 main.py ...

  7. 发票查验平台验证码识别

    国*税*局发票查验平台https://inv-veri.chinatax.gov.cn/, 验证码识别接口测试, 为了防止恶意使用, 每天限制接口调用次数为500 验证码须是原图, 不能从网页截图, ...

  8. MacBook Pro(苹果系统macOS)全国增值税发票查验平台无法获取验证码解决

    首先安装根证书,手动安装 下载证书得到 command+空格,进这个钥匙串访问app 将下载的文件拖进来安装好 我们返回验票网站,发现点击验证码还是不太行啊 https://fpcy.shanghai ...

  9. 【2020.06】国税总局发票查验平台验证码最新获取方法

    国税总局的发票查验平台近期JS更新频繁,之前写了一篇验证码识别的文章:https://blog.csdn.net/kerlomz/article/details/105974823 有不少人私信我,问 ...

  10. 国税局发票查验中英文验证码识别,识别率95.2%

    关于国税局发票验证码识别,应该是大多数从事发票查验的人员比较头疼的问题,但实际上发票验证码识别问题严格而言较为简单. 一.背景 首先,需要了解清楚国税局的发票验证码构成,如下图,正常而言都是中文.数字 ...

最新文章

  1. 已经入门了C++,后面的路怎么走?
  2. Oracle GoldenGate 之--异构平台同步(Mysql到Oracle)
  3. 第70天:jQuery基本选择器(一)
  4. java实现遍历文件夹下的文件及文件夹
  5. Python: 50个能够满足所有需要的模块
  6. python编程(多线程c回调python)
  7. 图片上添加文字--div
  8. Notification使用详解之二:可更新进度的通知
  9. [转]使用Git Submodule管理子模块
  10. bzoj 3036: 绿豆蛙的归宿(Dp)
  11. 使用jdk1.8 新特性stream实现多级分类
  12. LooseScan Strategy
  13. 旅游景点网站景区景点购票系统毕业设计毕业论文参考(1)功能概要
  14. 计算机网络原理的思维导图汇总
  15. linux自定义自动补全命令
  16. 【Opencv实战】纯手工代码打造车牌检测程序,秒变智能检测你值得拥有~(附源码)
  17. 移动端游戏开发:差异、挑战,以及全新的解决方案
  18. 基于PCA和PLS的近红外光谱建模
  19. SSL P1194 最优乘车 题目
  20. 《通信原理》多径衰落信道仿真2

热门文章

  1. java 8 stream 对集合的简单操作
  2. django.db.migrations.exceptions.BadMigrationError: Migration urls in app book has no Migration class
  3. java win7 管理员权限_win7系统获取管理员权限批处理的操作方法
  4. 又五年后回头再看我的程序员生涯
  5. 与门,或门,与非门,异或门的python实现
  6. docker服务假死解决方案
  7. Vue实现简单的音乐播放器
  8. 新浪微博技术架构分析 2010
  9. 2018再见|2019你好
  10. SHAMANIC Heil Amazonas-DschungelSHAMA