截屏就可以转文字?飞桨带您体验OCR超轻量中英文识别模型

【飞桨开发者说】陈千鹤,华中科技大学计算机科学与技术学院大一在读

截屏就可以转文字?飞桨带您体验OCR超轻量中英文识别模型_第1张图片

任务背景

目前很多实用小工具都趋向收费模式,即使免费,不是功能不完整,就是有很多约束条件,在应用时效果无法达到我们的预期。于是我萌生一个想法:结合自己学习的计算机技术和开源的AI算法,尝试动手实现一些常用小工具,而不再仅依赖商业软件的限制。

机缘巧合,我了解到百度飞桨开源了OCR超轻量中英文识别模型,该模型才8.6M,对内存要求小,预测速度快,可以很方便的在CPU上实现模型预测,甚至可实现在手机上部署,于是我决定利用这个模型开发一个截图取字的任务。大体思路如下:

  • 第一步:截取需要识别的图像,作为模型输入。在截图过程中,需要实现鼠标框选区域截图,并将图片以数据流的形式在程序中使用,无需保存文件;

  • 第二步:利用飞桨OCR超轻量中英文识别模型进行预测,它的优势是模型小、精度高;

  • 第三步:将模型预测结果以.txt格式存储,并自动呈现内容。

在实现过程中关键点一是截图,二是对OCR模型的运用。从模型预测效果看,通过OCR模型识别出来的文本信息与源文本基本完全一致,如下图所示。

截屏就可以转文字?飞桨带您体验OCR超轻量中英文识别模型_第2张图片

那什么是OCR呢?对于背景不熟悉的同学,在这里简单给大家背下书。

截屏就可以转文字?飞桨带您体验OCR超轻量中英文识别模型_第3张图片

通常情况下,我们可以根据OCR的应用场景差异将其划分为专用OCR和通用OCR。

专用OCR是指对特定场景进行设计和优化,以达到最好的效果展示,比较典型的应用如:证件识别、车牌识别。

截屏就可以转文字?飞桨带您体验OCR超轻量中英文识别模型_第4张图片

而通用OCR是指在更多、更复杂的场景下,可以拥有比较好的泛性。在这个过程中由于场景的不确定性,如:图片背景丰富、亮度不均衡、光照不均衡、残缺遮挡、文字扭曲、字体多样等等问题,都会给OCR识别会带来极大的挑战。

截屏就可以转文字?飞桨带您体验OCR超轻量中英文识别模型_第5张图片

截屏就可以转文字?飞桨带您体验OCR超轻量中英文识别模型_第6张图片

飞桨开源的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模型的效果是非常棒的。

截屏就可以转文字?飞桨带您体验OCR超轻量中英文识别模型_第7张图片

第三步:预测结果以.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超轻量中英文识别模型_第8张图片

相关参考

结果很有趣吧,大家也可以尝试识别其他图片信息,效果同样值得期待:)在科技的快速发展之下,基于OCR识别技术,则能实现图片文字信息的快速识别。它可以解决综合文字识别、视频文字识别、证件识别、票据识别、车牌Vin码识别、银行卡识别、云识别等多种费时费力的问题,使我们的生活更加便捷。

截图&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

你可能感兴趣的:(截屏就可以转文字?飞桨带您体验OCR超轻量中英文识别模型)