Python截屏工具,识别屏幕中的二维码

原创
10/14 15:49
阅读数 70

看网页的时候出现二维码怎么快速识别?一般人可能会掏出手机。手机识别有两个问题:1,对着屏幕,会有摩尔纹的干扰。2,识别结果不能在电脑上立刻使用。如果做个截屏小工具来识别,会方便很多。

安装Python依赖包

pip install pillow opencv-python dbr pyside2

屏幕截图识别二维码的步骤

  1. 用Qt搭建UI
  2. 创建一个自定义的全屏Qt widget
  3. 监听鼠标事件,获得区域
  4. 用PIL里的接口根据坐标抓图
  5. 调用Dynamsoft Barcode Reader的接口识别二维码

代码实现

打开UI编辑器(路径:Python37\Lib\site-packages\PySide2\designer.exe)创建界面。

  • 两个按钮分别用于区域截图和全屏截图
  • 中间区域是用于显示图片的QLabel

把UI编译成python文件:

pyside2-uic design.ui -o design.py

把UI导入到python工程中:

from design import Ui_MainWindow

class MainWindow(QMainWindow):

    def __init__(self, license):
        super(MainWindow, self).__init__()
        self.setWindowState(Qt.WindowMaximized)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.setAcceptDrops(True)

新建SnippingTool.py文件。在里面创建一个自定义的Qt Widget:

import numpy as np
import cv2
from PIL import ImageGrab
from PySide2 import QtWidgets, QtCore, QtGui
from PySide2.QtCore import Qt

class SnippingWidget(QtWidgets.QWidget):
    is_snipping = False

    def __init__(self, parent=None, app=None):
        super(SnippingWidget, self).__init__()
        self.parent = parent
        self.setWindowFlags(Qt.WindowStaysOnTopHint)

        self.screen = app.primaryScreen()
        self.setGeometry(0, 0, self.screen.size().width(), self.screen.size().height())
        self.begin = QtCore.QPoint()
        self.end = QtCore.QPoint()
        self.onSnippingCompleted = None

    def fullscreen(self):
        img = ImageGrab.grab(bbox=(0, 0, self.screen.size().width(), self.screen.size().height()))

        try:
            img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
        except:
            img = None
            
        if self.onSnippingCompleted is not None:
            self.onSnippingCompleted(img)

    def start(self):
        SnippingWidget.is_snipping = True
        self.setWindowOpacity(0.3)
        QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
        self.show()

    def paintEvent(self, event):
        if SnippingWidget.is_snipping:
            brush_color = (128, 128, 255, 100)
            lw = 3
            opacity = 0.3
        else:
            self.begin = QtCore.QPoint()
            self.end = QtCore.QPoint()
            brush_color = (0, 0, 0, 0)
            lw = 0
            opacity = 0

        self.setWindowOpacity(opacity)
        qp = QtGui.QPainter(self)
        qp.setPen(QtGui.QPen(QtGui.QColor('black'), lw))
        qp.setBrush(QtGui.QColor(*brush_color))
        rect = QtCore.QRectF(self.begin, self.end)
        qp.drawRect(rect)

    def mousePressEvent(self, event):
        self.begin = event.pos()
        self.end = self.begin
        self.update()

    def mouseMoveEvent(self, event):
        self.end = event.pos()
        self.update()

    def mouseReleaseEvent(self, event):
        SnippingWidget.is_snipping = False
        QtWidgets.QApplication.restoreOverrideCursor()
        x1 = min(self.begin.x(), self.end.x())
        y1 = min(self.begin.y(), self.end.y())
        x2 = max(self.begin.x(), self.end.x())
        y2 = max(self.begin.y(), self.end.y())

        self.repaint()
        QtWidgets.QApplication.processEvents()
        img = ImageGrab.grab(bbox=(x1, y1, x2, y2))

        try:
            img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
        except:
            img = None
            
        if self.onSnippingCompleted is not None:
            self.onSnippingCompleted(img)

        self.close()

在初始化的时候我们把widget设置成全屏大小:

self.screen = app.primaryScreen()
self.setGeometry(0, 0, self.screen.size().width(), self.screen.size().height())

在调用start()函数时,把widget显示出来。这个时候paintEvent()会被触发,我们在里面绘制截屏效果。

通过监听鼠标事件,我们可以获得点击和释放时候的坐标:

    def mousePressEvent(self, event):
        self.begin = event.pos()
        self.end = self.begin
        self.update()

    def mouseMoveEvent(self, event):
        self.end = event.pos()
        self.update()

    def mouseReleaseEvent(self, event):
        SnippingWidget.is_snipping = False
        QtWidgets.QApplication.restoreOverrideCursor()
        x1 = min(self.begin.x(), self.end.x())
        y1 = min(self.begin.y(), self.end.y())
        x2 = max(self.begin.x(), self.end.x())
        y2 = max(self.begin.y(), self.end.y())

        self.repaint()
        QtWidgets.QApplication.processEvents()

最后用ImageGrab.grab(bbox=(x1, y1, x2, y2))来获得屏幕的截图。全屏的坐标为img = ImageGrab.grab(bbox=(0, 0, self.screen.size().width(), self.screen.size().height()))

获得的图像经过转换交给回调函数传出:

        try:
            img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
        except:
            img = None
            
        if self.onSnippingCompleted is not None:
            self.onSnippingCompleted(img)

在主函数中,初始化按钮并设置回调函数。当截屏触发的时候,我们把窗口最小化,这样就不会遮挡屏幕。得到图像之后再把程序窗口还原:

self.snippingWidget = SnippingTool.SnippingWidget(app=QApplication.instance())
self.snippingWidget.onSnippingCompleted = self.onSnippingCompleted
self.ui.pushButton_area.clicked.connect(self.snipArea)
self.ui.pushButton_full.clicked.connect(self.snipFull)

def snipArea(self):
    self.setWindowState(Qt.WindowMinimized)
    self.snippingWidget.start()    

def snipFull(self):
    self.setWindowState(Qt.WindowMinimized)
    self.snippingWidget.fullscreen()    

回调函数触发之后,把图像数据传给SDK解码。最后把结果绘制到QLabel上:

    def onSnippingCompleted(self, frame):
        self.setWindowState(Qt.WindowMaximized)
        if frame is None:
            return 

        frame, self._results = self._barcodeManager.decode_frame(frame)
        self.showResults(frame, self._results)

在搜索引擎中找一批二维码图片做测试:

视频

https://www.bilibili.com/video/BV18P4y1t7Pp/

源码

https://github.com/yushulx/python-gui-barcode-reader

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部