【飞桨开发者说】陈千鹤,华中科技大学计算机科学与技术学院大一在读
任务背景
目前很多实用小工具都趋向收费模式,即使免费,不是功能不完整,就是有很多约束条件,在应用时效果无法达到我们的预期。于是我萌生一个想法:结合自己学习的计算机技术和开源的AI算法,尝试动手实现一些常用小工具,而不再仅依赖商业软件的限制。
机缘巧合,我了解到百度飞桨开源了OCR超轻量中英文识别模型,该模型才8.6M,对内存要求小,预测速度快,可以很方便的在CPU上实现模型预测,甚至可实现在手机上部署,于是我决定利用这个模型开发一个截图取字的任务。大体思路如下:
第一步:截取需要识别的图像,作为模型输入。在截图过程中,需要实现鼠标框选区域截图,并将图片以数据流的形式在程序中使用,无需保存文件;
第二步:利用飞桨OCR超轻量中英文识别模型进行预测,它的优势是模型小、精度高;
第三步:将模型预测结果以.txt格式存储,并自动呈现内容。
在实现过程中关键点一是截图,二是对OCR模型的运用。从模型预测效果看,通过OCR模型识别出来的文本信息与源文本基本完全一致,如下图所示。
那什么是OCR呢?对于背景不熟悉的同学,在这里简单给大家背下书。
通常情况下,我们可以根据OCR的应用场景差异将其划分为专用OCR和通用OCR。
专用OCR是指对特定场景进行设计和优化,以达到最好的效果展示,比较典型的应用如:证件识别、车牌识别。
而通用OCR是指在更多、更复杂的场景下,可以拥有比较好的泛性。在这个过程中由于场景的不确定性,如:图片背景丰富、亮度不均衡、光照不均衡、残缺遮挡、文字扭曲、字体多样等等问题,都会给OCR识别会带来极大的挑战。
飞桨开源的OCR模型集成在PaddleHub中,通过chinese_text_detection_db_server或者chinese_text_detection_db_mobile两个OCR中文识别Module,可以实现一键文字识别。更多OCR原理可以参考如下链接:
https://aistudio.baidu.com/aistudio/projectdetail/507159
文字识别任务操作过程
重点来了,下面我将详细介绍下截图取字任务的操作过程,感兴趣的小伙伴可以一起试试。
Python中有很多可以实现截图的库或者函数,如:
Python调用windows API实现屏幕截图:灵活、速度快,但是写法繁琐、不跨平台;
使用PIL的ImageGrab模块:速度慢,不太适合做即时截图;
Selenium截图:仅支持对Web(网页)截图。
但是上述三种方法仅能实现全屏截图或指定区域截图,而我们需要在鼠标框选范围截图,因此我采用重写PyQt5库的部分类实现截图功能。
PyQt5是一套Python绑定Digia QT5应用的框架,可用于Python 2和Python 3。PyQt5做为Python的一个模块,有620多个类和6000个函数和方法,是一个跨平台的工具包。可以运行在所有主流操作系统,如:UNIX、Windows、Mac OS。官网地址如下:
http://www.riverbankcomputing.co.uk/news。
想要实现鼠标框选范围截图,只需要把鼠标框选的起点和终点坐标传给Grab即可。那么如何获取鼠标框选的起点和终点呢?我们可以通过调用PyQt5并继承QWidget实现。
在代码演示前,先介绍下Qwidget的常用操作,如:点击鼠标、拖动&绘制截图框、释放鼠标,对应函数如下:
keyPressEvent(self, event):键盘响应函数
paintEvent(self, event):UI绘制函数
mousePressEvent(self, event):鼠标点击事件
mouseMoveEvent(self, event):鼠标移动事件
mouseReleaseEvent(self, event):鼠标释放事件
当然了,这部分有现成的轮子,可以直接使用,获取路径如下:
https://github.com/ianzhao05/textshot?u=5722964389&m=4508439520834016&cu=3655689037
class Snipper(QtWidgets.QWidget):
def __init__(self, parent=None, flags=Qt.WindowFlags()):
super().__init__(parent=parent, flags=flags)
self.setWindowTitle("")
self.setWindowFlags(
Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Dialog
)
self.setWindowState(self.windowState() | Qt.WindowFullScreen)
self.screen = QtGui.QScreen.grabWindow(
QtWidgets.QApplication.primaryScreen(),
QtWidgets.QApplication.desktop().winId(),
)
palette = QtGui.QPalette()
palette.setBrush(self.backgroundRole(), QtGui.QBrush(self.screen))
self.setPalette(palette)
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
self.start, self.end = QtCore.QPoint(), QtCore.QPoint()
def keyPressEvent(self, event):
if event.key() == Qt.Key_Escape:
QtWidgets.QApplication.quit()
return super().keyPressEvent(event)
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.setPen(Qt.NoPen)
painter.setBrush(QtGui.QColor(0, 0, 0, 100))
painter.drawRect(0, 0, self.width(), self.height())
if self.start == self.end:
return super().paintEvent(event)
painter.setPen(QtGui.QPen(QtGui.QColor(255, 255, 255), 3))
painter.setBrush(painter.background())
painter.drawRect(QtCore.QRect(self.start, self.end))
return super().paintEvent(event)
def mousePressEvent(self, event):
self.start = self.end = QtGui.QCursor.pos()
self.update()
return super().mousePressEvent(event)
def mouseMoveEvent(self, event):
self.end = QtGui.QCursor.pos()
self.update()
return super().mousePressEvent(event)
def mouseReleaseEvent(self, event):
if self.start == self.end:
return super().mouseReleaseEvent(event)
self.hide()
QtWidgets.QApplication.processEvents()
shot = self.screen.copy(QtCore.QRect(self.start, self.end))
processImage(shot)
QtWidgets.QApplication.quit()
def processImage(img):
buffer = QtCore.QBuffer()
buffer.open(QtCore.QBuffer.ReadWrite)
img.save(buffer, "PNG")
pil_img = Image.open(io.BytesIO(buffer.data()))
buffer.close()
if __name__ == '__main__':
QtCore.QCoreApplication.setAttribute(Qt.AA_DisableHighDpiScaling)
app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QMainWindow()
snipper = Snipper(window)
snipper.show()
sys.exit(app.exec_())
第二步:利用OCR模型进行预测,识别图片文字
在文字识别阶段,我使用飞桨OCR超轻量中英文识别模型。该模型同时支持中英文识别;支持倾斜、竖排等多种方向文字识别。值得推荐的是,当前该模型已经集成到飞桨PaddleHub中,通过预训练模型chinese_ocr_db_crnn_server或chinese_ocr_db_crnn_mobile即可实现一键预测。对于深度学习零基础的用户来讲,在操作上还是非常友好的。
更多OCR模型操作介绍可以参考如下链接:
https://www.paddlepaddle.org.cn/hub/scene/ocr
1. 将截取的图片传入模型。
import os
os.environ['HUB_HOME'] = "./modules"
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PIL import Image
import io
import sys
import numpy as np
import paddlehub as hub
class Snipper(QtWidgets.QWidget):
def __init__(self, parent=None, flags=Qt.WindowFlags()):
super().__init__(parent=parent, flags=flags)
self.setWindowTitle("TextShot")
self.setWindowFlags(
Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Dialog
)
self.setWindowState(self.windowState() | Qt.WindowFullScreen)
self.screen = QtGui.QScreen.grabWindow(
QtWidgets.QApplication.primaryScreen(),
QtWidgets.QApplication.desktop().winId(),
)
palette = QtGui.QPalette()
palette.setBrush(self.backgroundRole(), QtGui.QBrush(self.screen))
self.setPalette(palette)
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
self.start, self.end = QtCore.QPoint(), QtCore.QPoint()
def keyPressEvent(self, event):
if event.key() == Qt.Key_Escape:
QtWidgets.QApplication.quit()
return super().keyPressEvent(event)
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.setPen(Qt.NoPen)
painter.setBrush(QtGui.QColor(0, 0, 0, 100))
painter.drawRect(0, 0, self.width(), self.height())
if self.start == self.end:
return super().paintEvent(event)
painter.setPen(QtGui.QPen(QtGui.QColor(255, 255, 255), 3))
painter.setBrush(painter.background())
painter.drawRect(QtCore.QRect(self.start, self.end))
return super().paintEvent(event)
def mousePressEvent(self, event):
self.start = self.end = QtGui.QCursor.pos()
self.update()
return super().mousePressEvent(event)
def mouseMoveEvent(self, event):
self.end = QtGui.QCursor.pos()
self.update()
return super().mousePressEvent(event)
def mouseReleaseEvent(self, event):
if self.start == self.end:
return super().mouseReleaseEvent(event)
self.hide()
QtWidgets.QApplication.processEvents()
shot = self.screen.copy(QtCore.QRect(self.start, self.end))
processImage(shot)
QtWidgets.QApplication.quit()
def processImage(img):
buffer = QtCore.QBuffer()
buffer.open(QtCore.QBuffer.ReadWrite)
img.save(buffer, "PNG")
pil_img = Image.open(io.BytesIO(buffer.data()))
buffer.close()
np_images = [np.array(pil_img)]
results = ocr.recognize_text(
images=np_images, # 图片数据,ndarray.shape 为 [H, W, C],BGR格式;
use_gpu=False, # 是否使用 GPU;若使用GPU,请先设置CUDA_VISIBLE_DEVICES环境变量
output_dir='ocr_result', # 图片的保存路径,默认设为 ocr_result;
visualization=True, # 是否将识别结果保存为图片文件;
box_thresh=0.5, # 检测文本框置信度的阈值;
text_thresh=0.5) # 识别中文文本置信度的阈值;
text = []
for result in results:
data = result['data']
save_path = result['save_path']
for infomation in data:
print('text: ', infomation['text'], '\nconfidence: ', infomation['confidence'], '\ntext_box_position: ',
infomation['text_box_position'])
text.append(str(infomation['text']) + '\n')
print(text)
2. 加载预训练模型,进行预测。
if __name__ == '__main__':
# 加载移动端预训练模型
# ocr = hub.Module(name="chinese_ocr_db_crnn_mobile")
# 服务端可以加载大模型,效果更好
ocr = hub.Module(name="chinese_ocr_db_crnn_server")
QtCore.QCoreApplication.setAttribute(Qt.AA_DisableHighDpiScaling)
app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QMainWindow()
snipper = Snipper(window)
snipper.show()
sys.exit(app.exec_())
从预测结果看,输出信息中每一行文字的置信度都达到了0.96以上,可见飞桨OCR模型的效果是非常棒的。
第三步:预测结果以.txt形式自动呈现。
text = []
for result in results:
data = result['data']
save_path = result['save_path']
for infomation in data:
print('text: ', infomation['text'], '\nconfidence: ', infomation['confidence'], '\ntext_box_position: ',
infomation['text_box_position'])
text.append(str(infomation['text']) + '\n')
print(text)
with open('data.txt', 'w') as f:
for i in text:
f.write(str(i))
os.system(r'data.txt')
从模型预测效果看,通过OCR模型识别出来的文本信息与源文本基本完全一致。
相关参考
截图&OCR项目地址:
https://github.com/chenqianhe/screenshot_and_ocr
如果您想使用自定义数据训练超轻量模型,可以参考8.6M超轻量模型的打造方式,从PaddleOCR提供的基础算法库中选择适合自己的文本检测、识别算法,进行自定义的训练。PaddleOCR提供了详细的训练和模型串联指导:
https://github.com/PaddlePaddle/PaddleOCR/blob/develop/doc/customize.md
如在使用过程中有问题,可加入飞桨官方QQ群进行交流:703252161。
如果您想详细了解更多飞桨的相关内容,请参阅以下文档。
更多PaddleOCR的应用方法,欢迎访问项目地址:
GitHub:
https://github.com/PaddlePaddle/PaddleOCR
Gitee:
https://gitee.com/PaddlePaddle/PaddleOCR
官网地址:
https://www.paddlepaddle.org.cn
飞桨开源框架项目地址:
GitHub:
https://github.com/PaddlePaddle/Paddle
Gitee:
https://gitee.com/paddlepaddle/Paddle
END