文章目录
PaddleOCR
PaddleOCR 是百度基于飞桨(PaddlePaddle)框架开源的全场景文字识别工具,支持多语言、多场景、高精度的 OCR 能力,覆盖文本检测、识别、方向分类等全流程,广泛应用于文档扫描、车牌识别、票据处理、工业质检等场景。
项目介绍

图形化界面设计:
主要用于产品包装的编号进行识别确认,当编号对比不正确时,发出警报和闪光,并保存错误图片结果。
项目代码:
注意:要改成自己的端口号,和摄像头的固定ip地址才可以进行使用。
python
# 导入OpenCV库,用于计算机视觉任务,如图像和视频处理
import cv2
# 从collections模块导入Counter类,用于统计可迭代对象中元素的出现次数
from collections import Counter
# 导入os模块,用于与操作系统进行交互,如文件和目录操作
import os
# 从numpy模块导入ndarray类型,用于处理多维数组
from numpy import ndarray
# 导入sys模块,用于访问与Python解释器紧密相关的变量和函数
import sys
# 将指定路径添加到Python模块搜索路径中,以便可以导入该路径下的模块
sys.path.append(r"D:\MVS\Development\Samples\Python\MvImport")
# 从HKCamera_class模块导入HKCamera类,可能用于操作海康相机
from HKCamera_class import HKCamera
# 从PyQt5库导入QtCore、QtGui和QtWidgets模块,用于创建图形用户界面(GUI)
from PyQt5 import QtCore, QtGui, QtWidgets
# 从PyQt5.QtCore模块导入所有内容,方便使用其中的类和函数
from PyQt5.QtCore import *
# 从PyQt5.QtGui模块导入所有内容,用于处理图形、图像和字体等
from PyQt5.QtGui import *
# 从PyQt5.QtWidgets模块导入QFileDialog和QMainWindow类,QFileDialog用于文件选择对话框,QMainWindow是主窗口的基类
from PyQt5.QtWidgets import QFileDialog, QMainWindow
# 从金寨窗口0模块导入Ui_MainWindow类,可能是使用Qt Designer设计的主窗口界面类
from 金寨窗口0 import Ui_MainWindow
# 导入logging模块,用于记录程序运行时的信息
import logging
# 导入numpy库,用于科学计算和数组操作
import numpy as np
# 从paddleocr模块导入PaddleOCR类,用于进行光学字符识别(OCR)
from paddleocr import PaddleOCR
# 导入re模块,用于正则表达式操作,可用于字符串匹配和替换
import re
# 导入serial模块,用于串口通信
import serial
# 导入time模块,用于处理时间相关的操作,如延时
import time
# 导入argparse模块,用于解析命令行参数
import argparse
# 导入threading模块,用于创建和管理线程
import threading
# 定义一个函数,用于定时释放并重新打开摄像头资源
def release_capture2(cap):
while True:
# 每隔30分钟(1800秒)执行一次释放和重新打开操作
time.sleep(1800)
# 释放当前摄像头资源
cap.release()
# 重新打开指定编号的摄像头
cap.open(opt.cap_numb3)
# 创建一个命令行参数解析器对象
parser = argparse.ArgumentParser()
# 添加一个命令行参数,用于指定第二个串口的端口号,默认值为COM4
parser.add_argument("--SERIAL_PORT1", type=str, default='COM5', help='第二个串口的端口号')
# 添加一个命令行参数,用于指定第一个串口的端口号,默认值为COM5
parser.add_argument("--SERIAL_PORT2", type=str, default='COM6', help='第一个串口的端口号')
# 添加一个命令行参数,用于指定识别置信度水平,默认值为0.88
parser.add_argument("--confid_level", type=float, default=0.88, help='识别置信度水平')
# 添加一个命令行参数,用于指定第三个摄像头的编号,默认值为1
parser.add_argument("--cap_numb3", type=int, default=1, help='第三个摄像头编号')
# 添加一个命令行参数,用于指定获取帧的延迟时间,默认值为67
parser.add_argument("--frame_delay", type=int, default=67, help='获取帧的延迟时间')
# 解析命令行参数,并将结果存储在opt对象中
opt = parser.parse_args()
# 定义一些十六进制命令字符串,用于控制设备的灯光和蜂鸣器
LIGHT_BUZZ1 = "0110001A000101CE18" # 灯光+蜂鸣器1
LIGHT_BUZZ2 = "0110001A0001040E1B" # 灯光+蜂鸣器1
LIGHT = "0110001A0001028E19" # 灯光
BUZZ1 = "0110001A0001034FD9" # 蜂鸣器1
BUZZ2 = "0110001A000105CFDB" # 蜂鸣器2
BUZZ_CMD_CLOSE = "0110001A0001000FD8" # 关闭所有蜂鸣器
# 禁用调试级别的日志记录,减少不必要的日志输出
logging.disable(logging.DEBUG)
# 定义一个函数,用于向设备发送十六进制命令
def sendCmdToDevice(cmd, ser):
# 将十六进制字符串转换为字节对象
cmdd = bytes.fromhex(cmd)
# 通过串口发送字节数据到设备
ser.write(cmdd)
# 定义一个函数,用于找出列表中出现次数最多的元素
def most_common_element(lst):
# 使用Counter类统计列表中每个元素的出现次数
count = Counter(lst)
# 获取出现次数最多的元素
most_common_item = count.most_common(1)[0][0]
return most_common_item
# 定义一个函数,用于处理输入字符串,提取符合特定模式的部分
def process_string(input_string):
aa = []
# 使用空格分割输入字符串
parts = input_string.split()
# 定义正则表达式模式1:包含字母和数字,长度在2到10之间
pattern_alphanumeric = re.compile(r'^(?=.*[a-zA-Z])(?=.*\d)[a-zA-Z\d-]{2,10}$')
# 定义正则表达式模式2:由4到7位数字组成
pattern_at_least_two_digits = re.compile(r'^\d{4,7}$')
# 遍历分割后的每个部分
for part in parts:
# 检查是否符合模式1或模式2
if pattern_alphanumeric.match(part) or pattern_at_least_two_digits.match(part):
# 如果符合条件,则添加到结果列表中
aa.append(part)
return aa
# 定义一个函数,用于对字符串集合进行字符替换
def set_bing(set_a):
resu = set()
for j in set_a:
# 替换字符串中的特定字符
jj = j.replace('0', 'O').replace('o', 'O').replace('s', '5').replace('S', '5').replace('I', '1').replace('L', '1').replace('v', 'V').replace('B', '8').replace('p', 'P')
# 将替换后的字符串添加到结果集合中
resu.add(jj)
return resu
# 定义一个函数,用于隐藏标签并发送关闭蜂鸣器的命令
def hide_label_and_send_cmd(label, ser=None):
# 隐藏指定的标签
label.setVisible(False)
# 发送关闭蜂鸣器的命令
sendCmdToDevice(BUZZ_CMD_CLOSE, ser)
# 定义一个函数,用于检查某个文本在给定集合中出现的次数
def In_which(text, hun):
a = 0
for i in hun:
if text in i:
a += 1
return a
# 定义一个函数,用于计算四边形的面积
def are(i):
zs = i[0][0]
ys = i[0][1]
yx = i[0][2]
zx = i[0][3]
# 计算上底边的长度
width_A = np.sqrt(((zs[0] - ys[0]) ** 2) + ((zs[1] - ys[1]) ** 2))
# 计算下底边的长度
width_B = np.sqrt(((zx[0] - yx[0]) ** 2) + ((zx[1] - yx[1]) ** 2))
# 计算左侧边的长度
height_A = np.sqrt(((zs[0] - zx[0]) ** 2) + ((zs[1] - zx[1]) ** 2))
# 计算右侧边的长度
height_B = np.sqrt(((ys[0] - yx[0]) ** 2) + ((ys[1] - yx[1]) ** 2))
# 取上下底边的平均值作为宽度
width = (width_A + width_B) / 2
# 取左右侧边的平均值作为高度
height = (height_A + height_B) / 2
# 计算四边形的面积
area = width * height
return area
# 创建两个PaddleOCR对象,分别用于不同的OCR任务
ocr = PaddleOCR(use_angle_cls=True, use_gpu=True, lang='en', use_mp=True, total_process_num=8)
ocr2 = PaddleOCR(use_angle_cls=False, use_gpu=True, lang='en', use_mp=True, total_process_num=8)
# 定义一个继承自QMainWindow和Ui_MainWindow的类,用于创建主窗口
class PyQtMainEntry(QMainWindow, Ui_MainWindow):
def __init__(self):
# 调用父类的构造函数
super().__init__()
# 设置UI界面
self.setupUi(self)
# 打开第一个串口,波特率为9600,超时时间为2.5秒
self.ser1 = serial.Serial(opt.SERIAL_PORT1, 9600, timeout=2.5)
# 打开第二个串口,波特率为9600,超时时间为2.5秒
self.ser2 = serial.Serial(opt.SERIAL_PORT2, 9600, timeout=2.5)
# self.ser3 = serial.Serial(opt.SERIAL_PORT3, 9600, timeout=2.5)
# 隐藏指定的标签
self.label_4.setVisible(False)
self.label_5.setVisible(False)
self.label_6.setVisible(False)
# 初始化一些列表和集合,用于存储识别结果
self.list_zong1 = []
self.list_zong2 = []
self.list_zong3 = []
self.set_zong1 = set()
self.set_zong2 = set()
self.set_zong3 = set()
self.set_12hun = set()
self.guo = []
self.list1 = []
self.list2 = []
self.list3 = []
# 最大化窗口显示
# self.showMaximized()
# 初始化第一个海康相机,指定相机IP地址
self.camera1 = HKCamera(CameraIp='169.254.194.100')
# 设置相机的像素格式为BayerGB8
self.camera1.set_Value(param_type="enum_value", node_name="PixelFormat",
node_value='BayerGB8')
# 设置相机的增益自动调整模式为连续
self.camera1.set_Value(param_type="enum_value", node_name="GainAuto",
node_value='Continuous')
# 设置相机的采集帧率为15.0000
self.camera1.set_Value(param_type="float_value", node_name="AcquisitionFrameRate",
node_value='15.0000')
# 自动曝光设置,这里注释掉了
# self.camera1.set_Value(param_type="enum_value", node_name="ExposureAuto",
# node_value='Continuous') # 自动曝光
# 启动第一个相机
self.camera1.start_camera()
# 初始化第二个海康相机,指定相机IP地址
self.camera2 = HKCamera(CameraIp='169.254.194.200')
# 设置相机的像素格式为BayerGB8
self.camera2.set_Value(param_type="enum_value", node_name="PixelFormat",
node_value='BayerGB8')
# 设置相机的增益自动调整模式为连续
self.camera2.set_Value(param_type="enum_value", node_name="GainAuto",
node_value='Continuous')
# 设置相机的采集帧率为15.0000
self.camera2.set_Value(param_type="float_value", node_name="AcquisitionFrameRate",
node_value='15.0000')
# 自动曝光设置,这里注释掉了
# self.camera2.set_Value(param_type="enum_value", node_name="ExposureAuto",
# node_value='Continuous') #自动曝光
# 水平和垂直下采样设置,这里注释掉了
# self.camera2.set_Value(param_type="enum_value", node_name="DecimationHorizontal",
# node_value='2')
# self.camera2.set_Value(param_type="enum_value", node_name="DecimationVertical",
# node_value='2')
# 启动第二个相机
self.camera2.start_camera()
# 打开第三个摄像头,使用指定的摄像头编号
self.camera3 = cv2.VideoCapture(opt.cap_numb3)
# 创建一个线程,用于定时释放和重新打开第三个摄像头资源
release_thread2 = threading.Thread(target=release_capture2, args=(self.camera3,))
# 将线程设置为守护线程,主线程退出时该线程也会退出
release_thread2.daemon = True
# 启动线程
release_thread2.start()
# 初始化摄像头打开状态为False
self.is_camera_opened = False
# 创建一个Qt定时器对象
self._timer = QtCore.QTimer(self)
# 当定时器超时时,调用_queryFrame方法
self._timer.timeout.connect(self._queryFrame)
# 设置定时器的间隔时间,单位为毫秒
self._timer.setInterval(opt.frame_delay)
# 初始化帧计数器为0
self.frame_counter = 0
# 模拟点击按钮,这里注释掉了
# self.pushButton_4.click()
# 定义一个方法,用于打开或关闭摄像头
def openvideo(self):
# 切换摄像头打开状态
self.is_camera_opened = not self.is_camera_opened
if self.is_camera_opened:
# 如果摄像头打开,修改按钮文本为"关闭"
self.pushButton_4.setText("关闭")
# 启动定时器
self._timer.start()
else:
# 如果摄像头关闭,修改按钮文本为"打开"
self.pushButton_4.setText("打开")
# 停止定时器
self._timer.stop()
# 发送关闭蜂鸣器的命令到第一个串口
sendCmdToDevice(BUZZ_CMD_CLOSE, self.ser1)
# 发送关闭蜂鸣器的命令到第二个串口
sendCmdToDevice(BUZZ_CMD_CLOSE, self.ser2)
# 发送关闭蜂鸣器的命令到第三个串口,这里注释掉了
# sendCmdToDevice(BUZZ_CMD_CLOSE, self.ser3)
# 隐藏指定的标签
self.label_4.setVisible(False)
self.label_5.setVisible(False)
self.label_6.setVisible(False)
# 定义一个方法,用于打开指定文件夹
def open_folder(self):
# 定义文件夹路径
folder_path = r'D:\MVS\Development\Samples\Python\shiyan\baojing'
# 打开指定文件夹
QDesktopServices.openUrl(QUrl.fromLocalFile(folder_path))
# 定义一个方法,用于清空第一个集合和列表,并关闭蜂鸣器,隐藏标签
def clearSet1(self):
# 清空第一个集合
self.set_zong1.clear()
# 清空第一个列表,这里注释掉了
# self.list_zong1.clear()
# 清空第一个临时列表
self.list1.clear()
# 发送关闭蜂鸣器的命令到第一个串口
sendCmdToDevice(BUZZ_CMD_CLOSE, self.ser1)
# 隐藏指定的标签
self.label_4.setVisible(False)
# 定义一个方法,用于清空第二个集合和列表,并关闭蜂鸣器,隐藏标签
def clearSet2(self):
# 清空第二个集合
self.set_zong2.clear()
# 清空第二个列表,这里注释掉了
# self.list_zong2.clear()
# 清空第二个临时列表
self.list2.clear()
# 发送关闭蜂鸣器的命令到第二个串口
sendCmdToDevice(BUZZ_CMD_CLOSE, self.ser2)
# 隐藏指定的标签
self.label_5.setVisible(False)
# 定义一个装饰器函数,用于在函数调用n次后执行另一个函数
def execute_after_n_calls(n, w2set):
def decorator(func):
def wrapper(self, *args, **kwargs):
# 记录函数调用次数
wrapper.count += 1
# 调用原函数
result = func(self, *args, **kwargs)
if wrapper.count % n == 0:
# 当调用次数达到n的倍数时,执行指定的函数
w2set(self)
return result
# 初始化函数调用次数为0
wrapper.count = 0
# 返回包装函数
return wrapper
# 返回装饰器函数
return decorator
# 声明这是一个 PyQt 的槽函数,用于处理定时器超时事件,定期查询并处理摄像头帧
@QtCore.pyqtSlot()
def _queryFrame(self):
try:
# 尝试从第三个摄像头抓取一帧图像
if not self.camera3.grab():
# 如果抓取失败,打印提示信息
print("No frame grabbed.")
# 释放第三个摄像头资源
self.camera3.release()
# 关闭当前窗口
self.close()
else:
# 如果抓取成功,从第一个海康相机获取图像,并指定返回的图像类型为 ndarray
self.frame1: ndarray = self.camera1.get_image()
# 从第二个海康相机获取图像,并指定返回的图像类型为 ndarray
self.frame2: ndarray = self.camera2.get_image()
# 从第三个摄像头读取一帧图像,ret3 表示是否成功读取,self.frame3 为读取的图像
ret3, self.frame3 = self.camera3.read()
# 检查是否成功从第三个摄像头读取到图像
if not ret3:
# 如果读取失败,打印提示信息
print("No frame retrieved.")
# 注释掉的代码,原可能用于释放第一个相机资源
# self.camera1.release()
# 释放第三个摄像头资源
self.camera3.release()
# 关闭当前窗口
self.close()
else:
# 如果读取成功,将第一个相机的图像调整为 640x480 大小
self.frame11 = cv2.resize(self.frame1, (640, 480))
# 将第二个相机的图像调整为 640x480 大小
self.frame22 = cv2.resize(self.frame2, (640, 480))
# 将第三个摄像头的图像调整为 640x480 大小
self.frame33 = cv2.resize(self.frame3, (640, 480))
# 再次检查是否成功从第三个摄像头读取到图像
if ret3:
# 将第一个相机调整大小后的图像从 BGR 颜色空间转换为 RGB 颜色空间
qimage = cv2.cvtColor(self.frame11.copy(), cv2.COLOR_BGR2RGB)
# 将转换后的图像数据封装为 QtGui.QImage 对象
qimage = QtGui.QImage(qimage.data, qimage.shape[1], qimage.shape[0], QtGui.QImage.Format_RGB888)
# 将 QImage 对象转换为 QPixmap 对象
pixmap = QtGui.QPixmap.fromImage(qimage)
# 将第二个相机调整大小后的图像从 BGR 颜色空间转换为 RGB 颜色空间
qimage2 = cv2.cvtColor(self.frame22.copy(), cv2.COLOR_BGR2RGB)
# 将转换后的图像数据封装为 QtGui.QImage 对象
qimage2 = QtGui.QImage(qimage2.data, qimage2.shape[1], qimage2.shape[0], QtGui.QImage.Format_RGB888)
# 将 QImage 对象转换为 QPixmap 对象
pixmap2 = QtGui.QPixmap.fromImage(qimage2)
# 将第三个摄像头调整大小后的图像从 BGR 颜色空间转换为 RGB 颜色空间
qimage3 = cv2.cvtColor(self.frame33.copy(), cv2.COLOR_BGR2RGB)
# 将转换后的图像数据封装为 QtGui.QImage 对象
qimage3 = QtGui.QImage(qimage3.data, qimage3.shape[1], qimage3.shape[0], QtGui.QImage.Format_RGB888)
# 将 QImage 对象转换为 QPixmap 对象
pixmap3 = QtGui.QPixmap.fromImage(qimage3)
# 将第一个相机图像对应的 QPixmap 对象设置到名为 label 的标签上进行显示
self.label.setPixmap(pixmap)
# 将第二个相机图像对应的 QPixmap 对象设置到名为 label_3 的标签上进行显示
self.label_3.setPixmap(pixmap2)
# 将第三个摄像头图像对应的 QPixmap 对象设置到名为 label_2 的标签上进行显示
self.label_2.setPixmap(pixmap3)
# 每处理一帧图像,帧计数器加 1
self.frame_counter += 1
# 检查帧计数器是否是 5 的倍数
if self.frame_counter % 5 == 0:
# 如果是 5 的倍数,调用 _performOCR1 方法进行 OCR 识别
self._performOCR1()
# 检查帧计数器是否是 3 的倍数
if self.frame_counter % 3 == 0:
# 如果是 3 的倍数,调用 _performOCR2 方法进行 OCR 识别
self._performOCR2()
# 注释掉的代码,原可能用于调用 OCR 识别方法
# self._performOCR1()
# self._performOCR2()
# self._performOCR3()
# 再次检查帧计数器是否是 3 的倍数
if self.frame_counter % 3 == 0:
# 如果是 3 的倍数,调用 _performOCR3 方法进行 OCR 识别
self._performOCR3()
except:
# 捕获所有异常,不做具体处理,避免程序因异常而崩溃
pass
# 定义一个方法 w2set1,用于处理第一个相机的 OCR 识别结果,进行结果分析、显示和异常处理
def w2set1(self):
# 初始化最终答案为空字符串
self.daan1 = ''
# 检查存储第一个相机 OCR 识别结果的列表是否不为空
if self.list_zong1:
try:
# 遍历存储第一个相机 OCR 识别结果的列表
for i in self.list_zong1:
# 检查元素 i 在列表中出现的次数是否大于 1
if self.list_zong1.count(i) > 1:
# 如果出现次数大于 1,将该元素添加到第一个相机结果集合中
self.set_zong1.add(i)
# 对第一个相机结果集合进行字符替换处理,得到处理后的集合
set_len1 = set_bing(self.set_zong1) # 当前的结果
# 遍历处理后的集合
for j in set_len1:
# 将集合中的元素添加到临时列表 list1 中
self.list1.append(j)
# 找出临时列表 list1 中出现次数最多的元素,作为最终答案
self.daan1 = most_common_element(self.list1)
# 检查帧计数器是否是 16 的倍数,大约每 10 秒刷新一次临时列表
if self.frame_counter % 16 == 0:
# 如果是 16 的倍数,清空临时列表 list1
self.list1.clear()
# 将处理后的集合中的元素用逗号连接成一个字符串
text = ','.join(set_len1)
# 将连接后的字符串显示在名为 lineEdit 的文本框中
self.lineEdit.setText(text)
# 进行结果判断
# 如果最终答案为空或者处理后的集合为空,不做任何处理
if (not self.daan1) or (not set_len1):
pass
# 如果处理后的集合中只有一个元素,且该元素等于最终答案,不做任何处理
elif len(set_len1) == 1 and (list(set_len1)[0] == self.daan1):
pass
else:
# 如果不满足上述条件,打印提示信息
print('一')
# 打印处理后的集合和最终答案
print(set_len1, self.daan1)
# 在第一个相机的图像副本上添加文本信息,文本内容为处理后的集合元素连接成的字符串
tu1 = cv2.putText(self.frame1.copy(), text, (100, 200), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 5,
cv2.LINE_AA)
# 获取当前时间,格式化为指定的字符串
nam = time.strftime("%Y_%m_%d_%H_%M", time.localtime())
# 构建保存异常图像的文件路径
path = os.path.join(r'D:\MVS\Development\Samples\Python\shiyan\baojing\相机一',
f'{nam}-{self.daan1}.jpg')
# 将添加文本后的图像保存到指定路径
cv2.imencode('.jpg', tu1)[1].tofile(path)
# 显示名为 label_4 的标签,可能用于提示异常信息
self.label_4.setVisible(True)
# 发送打开灯光的命令到第一个串口,可能用于触发灯光提示
# sendCmdToDevice(LIGHT_BUZZ1, self.ser1)
sendCmdToDevice(LIGHT, self.ser1)
# 5 秒后隐藏标签并发送关闭蜂鸣器的命令
QtCore.QTimer.singleShot(5000, lambda: hide_label_and_send_cmd(self.label_4, self.ser1))
# 清空存储第一个相机 OCR 识别结果的列表和集合
# self.list_zong1.clear()
# self.set_zong1.clear()
# 重置帧计数器为 0
self.frame_counter = 0
except Exception as e:
# 捕获异常并打印异常信息
print(e)
# 清空存储第一个相机 OCR 识别结果的列表和集合
self.list_zong1.clear()
self.set_zong1.clear()
# 使用 execute_after_n_calls 装饰器,使得 _performOCR1 方法每调用 5 次后执行 w2set1 方法
@execute_after_n_calls(5, w2set1)
# 定义一个方法 _performOCR1,用于对第一个相机的图像进行 OCR 识别
def _performOCR1(self):
# 使用 ocr2 对第一个相机的图像进行 OCR 识别,不进行角度分类
result = ocr2.ocr(self.frame1, cls=False)
# 检查识别结果是否不为空,且结果中不包含 None
if result and not None in result:
try:
# 遍历识别结果中的每一项
for i in result[0]:
# 打印当前识别项
# print(i)
# 计算当前识别项所包围区域的面积
mianji = are(i)
# 打印当前识别项和其面积
# print('yi',i,mianji)
# 对当前识别项的文本内容进行处理,提取符合条件的部分
ma = process_string(i[1][0])
# 检查识别置信度是否大于 0.92,处理后的文本是否不为空,识别区域的左上角 x 坐标是否在 320 到 1060 之间,且面积是否大于 5000
# if i[1][1] > 0.92 and ma and 80<i[0][0][0]<550 and mianji>1000:
if i[1][1] > 0.92 and ma and 320 < i[0][0][0] < 1060 and mianji > 5000:
# 如果满足条件,将处理后的文本添加到存储第一个相机 OCR 识别结果的列表中
self.list_zong1.extend(ma)
# 打印当前识别项的文本内容和其面积
# print(i[1][0], are(i))
# 打印分割线,用于区分不同帧的识别结果
# print('******************')
except:
# 捕获异常,不做具体处理
pass
# 定义处理第二个相机OCR结果的方法,进行结果分析、显示和异常处理
def w2set2(self):
self.daan2 = '' # 初始化最终答案为空字符串
if self.list_zong2: # 检查存储第二个相机OCR结果的列表是否有数据
try:
# 遍历列表,统计出现次数超过1次的元素并存入集合
for i in self.list_zong2:
if self.list_zong2.count(i) > 1:
self.set_zong2.add(i)
# 对集合中的字符进行替换处理
set_len2 = set_bing(self.set_zong2)
# 将处理后的字符添加到临时列表
for j in set_len2:
self.list2.append(j)
# 找出临时列表中出现次数最多的元素作为最终答案
self.daan2 = most_common_element(self.list2)
# 每25帧清空临时列表(约14秒刷新)
if self.frame_counter % 25 == 0:
self.list2.clear()
# 将结果显示在文本框中
text2 = ','.join(set_len2)
self.lineEdit_2.setText(text2)
# 结果验证逻辑
if (not self.daan2) or (not set_len2):
pass # 结果为空时不处理
elif len(set_len2) == 1 and (list(set_len2)[0] == self.daan2):
pass # 唯一结果时不处理
else:
print('二') # 打印调试信息
print(set_len2, self.daan2) # 输出当前结果和最终答案
# 在图像上添加文本标注
tu2 = cv2.putText(self.frame2.copy(), text2, (100, 200),
cv2.FONT_HERSHEY_SIMPLEX, 3, (0, 0, 255), 6, cv2.LINE_AA)
# 保存异常图像
nam2 = time.strftime("%Y_%m_%d_%H_%M", time.localtime())
path = os.path.join(r'D:\MVS\Development\Samples\Python\shiyan\baojing\相机二',
f'{nam2}-{self.daan2}.jpg')
cv2.imencode('.jpg', tu2)[1].tofile(path)
# 显示报警标签并控制灯光
self.label_5.setVisible(True)
sendCmdToDevice(LIGHT, self.ser2) # 打开灯光
# 5秒后关闭灯光并隐藏标签
QtCore.QTimer.singleShot(5000, lambda: hide_label_and_send_cmd(self.label_5, self.ser2))
# 重置帧计数器
self.frame_counter = 0
except Exception as e:
print(e) # 异常处理
finally:
# 清空临时数据
self.list_zong2.clear()
self.set_zong2.clear()
# 合并两个相机的识别结果
self.set_12hun = set([self.daan1, self.daan2])
print('1:', self.daan1) # 打印调试信息
print('2:', self.daan2)
print('合:', self.set_12hun)
# 使用装饰器控制OCR执行频率(每3次调用后处理结果)
@execute_after_n_calls(3, w2set2)
def _performOCR2(self):
result2 = ocr2.ocr(self.frame2, cls=False) # 执行OCR识别
if result2 and not None in result2:
try:
for i in result2[0]:
mianji = are(i) # 计算识别区域面积
ma = process_string(i[1][0]) # 文本处理
# 置信度、文本有效性、区域位置和面积过滤条件
if i[1][1] > 0.84 and ma and mianji > 1600 and (350 < i[0][0][0] < 1060):
self.list_zong2.extend(ma) # 保存有效识别结果
except:
pass
elif None in result2:
# 处理识别结果包含None的情况
self.daan2 = ''
self.set_12hun = set([self.daan1, self.daan2])
print('1:', self.daan1)
print('2:', self.daan2)
print('合:', self.set_12hun)
# 定义处理第三个相机OCR结果的方法
def w2set3(self):
maxx = 0 # 记录最大面积
if self.list_zong3:
try:
# 提取所有识别文本并统计出现次数
for i in self.list_zong3:
self.guo.append(i[1][0])
for i in self.guo:
if self.guo.count(i) > 1:
self.set_zong3.add(i)
# 找出最大面积的有效识别结果
for j in self.list_zong3:
if (j[1][0] in self.set_zong3) and (are(j) > maxx):
aa = j
maxx = are(j)
# 文本处理并显示
text3 = aa[1][0].replace('0', 'O').replace('o', 'O').replace('S', '5').replace('I', '1').replace('L',
'1').replace(
'v', 'V').replace('B', '8').replace('p', 'P')
self.lineEdit_3.setText(text3)
# 结果验证逻辑
if (not text3) or self.set_12hun == set() or self.set_12hun == {''}:
pass # 无效结果不处理
elif self.set_12hun and In_which(text3, self.set_12hun):
pass # 匹配到合并结果不处理
else:
print('三') # 打印调试信息
print(text3)
print(self.set_12hun)
# 在图像上添加文本标注
tu3 = cv2.putText(self.frame3.copy(), text3, (100, 200),
cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 5, cv2.LINE_AA)
# 保存异常图像
nam3 = time.strftime("%Y_%m_%d_%H_%M", time.localtime())
path = os.path.join(r'D:\MVS\Development\Samples\Python\shiyan\baojing\相机三',
f'{nam3}-{self.set_12hun}.jpg')
cv2.imencode('.jpg', tu3)[1].tofile(path)
# 重置合并结果并触发报警
self.set_12hun.clear()
self.label_6.setVisible(True)
sendCmdToDevice(LIGHT, self.ser2) # 打开灯光
QtCore.QTimer.singleShot(5000, lambda: hide_label_and_send_cmd(self.label_6, self.ser2))
except:
pass
finally:
# 清空临时数据
self.guo.clear()
self.list_zong3.clear()
self.set_zong3.clear()
# 使用装饰器控制OCR执行频率(每3次调用后处理结果)
@execute_after_n_calls(3, w2set3)
def _performOCR3(self):
global text3
try:
# 颜色检测预处理
hsv_image = cv2.cvtColor(self.frame3, cv2.COLOR_BGR2HSV)
lower_brown = np.array([10, 30, 30])
upper_brown = np.array([30, 255, 255])
mask = cv2.inRange(hsv_image, lower_brown, upper_brown)
white_pixels = np.sum(mask == 255)
total_pixels = mask.shape[0] * mask.shape[1]
color_percentage = (white_pixels / total_pixels) * 100
# 当棕色区域超过20%时执行OCR
if color_percentage >= 20:
result2 = ocr2.ocr(self.frame3, cls=False)
if result2 and not None in result2:
for j in result2[0]:
# 置信度和文本有效性过滤
if j[1][1] > 0.93 and process_string(j[1][0]):
self.list_zong3.append(j)
else:
text3 = ''
except:
text3 = ''
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv) # 创建Qt应用程序实例
window = PyQtMainEntry() # 创建主窗口实例
window.show() # 显示主窗口
sys.exit(app.exec_()) # 进入应用程序主循环