【飞桨开发者说】邵申辰,2018级计算机科学与技术专业,华北航天工业大学,PPDE,飞桨开发者技术专家,2020年国家大学生创新创业训练计划市级项目,研究方向为计算机视觉。
项目背景
Windows操作系统拥有数量庞大的用户。 凭借其优秀的人机操作、更好的软硬件支持以及前期抢占的市场,到2020年,其全球用户数将突破10亿。
为了实现高效办公,勤奋的开发者创造了很多软件来满足你的需求。比如个别网页的文字受JavaScript限制,无法复制,百度也解决不了好久(是的,我太擅长了)
)。 为什么不尝试一下暴力的方法,直接将笔记本上的文字识别为图片呢? 但很多软件都是要钱的,而且疗效一般都是免费的,或者每天晚上都会给你几次体验的机会。 最重要的一点是里面的软件可以识别本地图片。 就像共享单车解决了城市最后一公里一样,截图识别省去了保存图片上传识别的过程。
该项目的AIStudio地址:
说到识别模型,就不得不提PaddleOCR,它连续几天霸占了GitHub上的Trending榜单。 PaddleOCR是百度开源的超轻量级OCR模型库。 提供数十种文本检查识别模型,从而打造丰富、领先、实用的文本检查识别模型/工具库,促进用户更好的训练。 模型并将其应用到地面。 值得注意的是,PaddleHub 中添加了使用 DB 文本检查和 CRNN 文本识别的预训练模型 chinese_ocr_db_crnn_mobile,因此可以使用该 hub 轻松获取并在代码中使用该模型。
图1-免费OCR软件的部分界面
整个想法
既然要做一个截图识别软件,那么首先需要截取截图,并将内容识别交给模型,最后将识别结果输出到文本框。 截图和结果输出很容易,主要问题是使用哪些模型来测量和识别文本?
基于分割的测量自然场景文本的方法似乎越来越受欢迎javascript 字符截取,因为分割网络的结果可以准确地描述扭曲文本等场景。 基于分割的方法的关键步骤是后处理部分,它将分割结果转换为文本框或文本区域。 DB文本检查算法也是基于分割的,提出了可微分二值化(DB)来简化分割后的处理步骤,并且可以设置自适应阈值来提高网络性能。
图2-DB网络架构
模型的网络结构如图2所示,对输入图像进行不同阶段的采样,得到不同的特征图,然后利用这个特征图构建特征金字塔,最终输出大小为1的特征图F /4,并用它同时预测概率图P和阈值图T,并根据P和T估计后得到近似二值图B。如上所述,DB可以对每个进行自适应二值化像素。 阈值是从网络中学习的,二值化的步骤完全加入到网络中一起训练。 二值化公式如下,k为放大倍数根据经验设置为50。
图3-二值化公式与sigmoid比较
损失函数由概率图损失、二值图损失和阈值图损失组成。 其中,α和β分别设置为1和10。 并使用二元交叉熵损失函数,并使用L1距离损失函数。
标签的生成基于PSENet形式,收缩比列r设置为0.4,文本框向外收缩和扩展D个像素,然后估计每个收缩框和扩展框的差异框和扩展框之间差异的像素。 原始图像边界的归一化距离。 下式中的S和C分别代表面积和边长。
说完了检查算法,接下来就该说一下识别算法了。 一种流行且简单的识别算法是CRNN(AnEnd-to-EndTrainableNeuralNetworkforImage-basedSequenceRecognitionandItsApplicationtoSceneTextRecognition),其网络结构如下:
图4-CRNN网络架构
从下往上看,第一部分是频域层,利用VGG来提取输入图像的特征。 VGG是一种经典的频域神经网络。 它的规则非常简单:使用几个填充为1的3×3频域层和最大池化层的窗口形状。 频域层保持输入的高和宽不变,通道数加倍,池化层高和宽减半,最后用全连接和激活函数softmax连接实现分类。
图5-VGG模型
VGG 与之前网络的区别在于,它用连续的 3×3 频域核代替了较大的频域核(11×11、7×7、5×5),这样不仅减少了参数,还带来了同样的效果。疗效如同一般经验场。
第二部分利用LSTM进一步提取图像频域特征中的序列特征。 文本识别主要有两种方法,一种是基于字符/短语的识别,另一种是基于序列的识别。 基于字/词组识别,将文本段中的每个字/词组通过剪切的方式分别提取出来,然后进行识别,本质上是图像分类。 而剪切的质量直接影响识别结果,如果不能准确地剪切出字词,就无法得到识别结果。 基于序列的识别将文本识别视为序列识别,这样可以保证算法不会漏掉字符/短语,同时还可以结合上下文更好地处理序列信息。
图 6 - 基于字符/短语的识别
图 7 - 基于序列的识别
然而RNN的输出序列X与真实标签序列很难严格对齐,因此引入CTC来解决训练时字符不对齐的问题。 为了更好地理解CTC比对,这里举一个简单的反例。 假设我们使用CNN+RNN(GRU)来预测图5中的文本,输出结果不是我们期望的“飞桨”,有些部分重叠,有些部分因为文本之间有空格而空白。 事实上,CRNN是由不同的网络架构组成的,但是可以通过损失函数来联合训练。 该模型的更多细节可以在原始论文中找到:
我们利用PyQt的可视化功能,将屏幕截图读取和文本识别集成到pushButton控件中,并将输出结果放在textEdit控件中,制作一个简单的GUI。 源代码以及如何使用它可以在 中找到。
代码
项目的代码结构如下,requirements.txt是需要安装的模块。
图 8 - 代码结构
这里没有逐行敲出PyQt的用户界面代码,而是用QtDesigner生成.ui文件,然后在命令行用python-mpyuic5-ointerface.pyinterface.ui转换成.py文件。
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(500, 500)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setObjectName("pushButton")
self.verticalLayout.addWidget(self.pushButton)
self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
self.textEdit.setObjectName("textEdit")
self.verticalLayout.addWidget(self.textEdit)
self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 273, 22))
self.menubar.setObjectName("menubar")
self.menufile = QtWidgets.QMenu(self.menubar)
self.menufile.setObjectName("menufile")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.menubar.addAction(self.menufile.menuAction())
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "OCR截屏文字识别助手"))
self.pushButton.setText(_translate("MainWindow", "识别文字"))
self.menufile.setTitle(_translate("MainWindow", "file"))
截图识别过程分为三个步骤:
阅读截图;
使用PaddleOCR识别截图;
将识别结果复制到textEdit控件中。
Python自带的ImageGrab库提供了一种读取屏幕截图的方法,只需一行代码即可完成。 MAC系统中还有截图功能,且无法通过该功能读取,因此很难实现完整的识别功能。
为了减少代码量,这里直接使用PaddleHub提供的OCR模型。 需要说明的是,该模型依赖第三方库shapely和pyclipper,使用前请先安装这两个库。 该中心提供两种运行模型的方式 - 命令行和 API。 命令行操作很简单,只需一行代码:$hubrunchinese_ocr_db_crnn_mobile--input_path "/PATH/TO/IMAGE"。 然而,当我们在Python代码中使用hub时,我们仍然关注API方法:
def recognize_text(images=[],
paths=[],
use_gpu=False,
output_dir='ocr_result',
visualization=False,
box_thresh=0.5,
text_thresh=0.5)
范围
paths为图像的路径,值为字符; images 为读取后图像的格式,值为ndarray。 因为我们的截图是ImageGrab.grabclipboard()函数直接读取的ndarray格式,所以我们需要使用images参数。 如果是本地图片,可以通过paths参数指定,不需要使用cv2.imread等方法读取图片然后使用。
预测结果以JSON格式保存:
我们只需要识别文本内容,因此将文本值记录在data中。
from interface import Ui_MainWindow
from PIL import Image, ImageGrab
import cv2
import numpy as np
from PyQt5.QtWidgets import QApplication, QMainWindow
import paddlehub as hub
class run(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
# 读取截图,返回截图
def imread(self, ui):
img = ImageGrab.grabclipboard()
try:
img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
except TypeError:
ui.textEdit.setText("请先截图再点击按钮!")
return
else:
return img
# 使用PPOCR识别截图,返回识别结果
def imrec(self, img):
ocr = hub.Module(name="chinese_ocr_db_crnn_mobile")
result = ocr.recognize_text(images=[img])
res = str()
for data in result[0]["data"]:
res += data["text"]
return res
# 将识别结果打印到文本框中
def print_res(self, ui, res):
ui.textEdit.setText(res)
def all(self, ui):
img = self.imread(ui)
if not isinstance(img, np.ndarray):
return
res = self.imrec(img)
self.print_res(ui, res)
main.py作为整个PyQt5的入口javascript 字符截取,以信号和槽的形式连接识别算法。
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
import interface
import screen_rec
if __name__ == '__main__':
app = QApplication(sys.argv)
# 生成主框口
MainWindow = QMainWindow()
# 定义自己设计的ui
ui = interface.Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
# 向槽函数传递ui即可修改textEdit控件
ui.pushButton.clicked.connect(lambda: screen_rec.run().all(ui))
sys.exit(app.exec_())
演示
请在Windows 10操作系统下运行该软件。 MAC的截图功能其实可以用Command+Shift+4来使用,但是获得的图片无法粘贴保存,因此无法通过该功能读取。
建议使用 Miniconda 来管理你的包和环境,以避免奇怪的版本兼容性问题。
Win+Shift+S 弹出截图标志;
成功截取图片后,点击软件的识别文字即可开始识别;
识别完成后,在下方文本框中输出可编辑的识别结果。
图 9 - 动画演示
总结与展望
经测试,在AMD Ryzen 2500U处理器中,识别200个字符需要9秒,识别1000个字符需要25秒,而AIStudio提供的Intel(R) Xeon(R) Gold6148处理器分别需要2秒和12秒。 由于其紧凑的型号规格。
有时候打字费时费力,截图识别可以将截图翻译成可编辑的文本,大大提高了生产率。 值得注意的是,该项目仍在迭代中,仍有一些可以优化的地方。 比如显存泄漏问题,识别完成后,显存并不会完全释放。 考虑到PyQt主界面没有关闭,加载的模型没有被删除,导致显存占用。 对于CPU性能较弱、识别时间较长的本地机器,可以考虑接入百度的OCR字符识别API,实现准确、快速的识别。
如果您在使用过程中遇到任何问题,可以加入飞桨官方QQ群进行交流:1108045677。