在工业生产检测场景中快速、准确识别产品型号并对异常型号及时报警是保障产品质量和生产效率的关键环节。本文将分享一套基于 Python、OpenCV、PaddleOCR 和 PyQt5 实现的多摄像头产品型号识别与报警系统该系统可实时采集多路摄像头画面通过 OCR 识别产品型号对比标准型号后触发声光报警并保存异常画面。一、系统整体架构系统核心功能分为三大部分摄像头数据采集支持海康工业相机网口和 USB 摄像头实时获取多路画面OCR 型号识别基于 PaddleOCR 实现字符识别针对工业场景字符易混淆问题做专项优化异常检测与报警对比识别结果与基准型号异常时触发声光报警并保存取证画面。系统整体流程摄像头画面采集 → 帧间隔OCR识别 → 字符清洗与标准化 → 型号对比 → 异常则声光报警画面存档二、核心技术选型表格技术 / 库用途PyQt5可视化界面搭建摄像头画面展示按钮交互OpenCV图像预处理、摄像头数据读取、画面绘制PaddleOCR光学字符识别支持中英文、角度分类serial串口通信控制声光报警器threading后台线程管理避免界面卡顿numpy图像数据处理、字符区域面积计算三、核心代码实现1. 环境准备首先安装依赖包pip install opencv-python pyqt5 paddleocr numpy pyserial2. 系统初始化核心类定义import cv2 from collections import Counter import os from numpy import ndarray import sys import logging import numpy as np from paddleocr import PaddleOCR import re import serial import time import argparse import threading from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import QFileDialog, QMainWindow # 报警指令定义 LIGHT_BUZZ1 0110001A000101CE18 # 闪光声音1 LIGHT 0110001A0001028E19 # 仅闪光 BUZZ_CMD_CLOSE 0110001A0001000FD8 # 关闭报警 # 初始化OCR引擎 ocr PaddleOCR(use_angle_clsTrue,use_gpuTrue, langen) ocr2 PaddleOCR(use_angle_clsFalse, use_gpuTrue, langen) # 命令行参数解析 parser argparse.ArgumentParser() parser.add_argument(--SERIAL_PORT1, typestr, defaultCOM5, help第一个报警器的串口号) parser.add_argument(--SERIAL_PORT2, typestr, defaultCOM4, help第二/三个报警器的串口号) parser.add_argument(--confid_level, typefloat, default0.88, help识别的置信度) parser.add_argument(--cap_numb3, typeint, default1, help第三个摄像头编号) parser.add_argument(--frame_delay, typeint, default67, help获取画面帧数的延时) opt parser.parse_args() class PyQtMainEntry(QMainWindow, Ui_MainWindow): def __init__(self): super().__init__() self.setupUi(self) # 初始化串口连接报警器 self.ser1 serial.Serial(opt.SERIAL_PORT1, 9600, timeout2.5) self.ser2 serial.Serial(opt.SERIAL_PORT2, 9600, timeout2.5) # 初始化UI元素隐藏报警标签 self.label_4.setVisible(False) # 摄像头1异常标签 self.label_5.setVisible(False) # 摄像头2异常标签 self.label_6.setVisible(False) # 摄像头3异常标签 # 初始化数据存储容器 self.list_zong1 [] # 摄像头1识别结果列表 self.list_zong2 [] # 摄像头2识别结果列表 self.list_zong3 [] # 摄像头3识别结果列表 self.set_zong1 set() self.set_zong2 set() self.set_zong3 set() self.set_12hun set() # 摄像头12基准型号集合 # 初始化摄像头 # 海康工业相机1 self.camera1 HKCamera(CameraIp192.168.20.20) self.camera1.set_Value(param_typeenum_value, node_namePixelFormat, node_valueBayerGB8) self.camera1.set_Value(param_typeenum_value, node_nameGainAuto, node_valueContinuous) self.camera1.set_Value(param_typefloat_value, node_nameAcquisitionFrameRate, node_value15.0000) self.camera1.start_camera() # 海康工业相机2 self.camera2 HKCamera(CameraIp192.168.20.40) self.camera2.set_Value(param_typeenum_value, node_namePixelFormat, node_valueBayerGB8) self.camera2.set_Value(param_typeenum_value, node_nameGainAuto, node_valueContinuous) self.camera2.set_Value(param_typefloat_value, node_nameAcquisitionFrameRate, node_value15.0000) self.camera2.start_camera() # USB摄像头3 self.camera3 cv2.VideoCapture(opt.cap_numb3) # 启动后台线程定期释放USB摄像头资源防止内存泄漏 release_thread2 threading.Thread(targetself.release_capture3, args(self.camera3,)) release_thread2.daemon True release_thread2.start() # 初始化定时器用于画面刷新 self.is_camera_opened False self._timer QtCore.QTimer(self) self._timer.timeout.connect(self._queryFrame) self._timer.setInterval(opt.frame_delay) self.frame_counter 0 # 帧数计数器3. 关键工具函数1串口指令发送控制报警器def sendCmdToDevice(cmd, ser): 向报警器发送串口指令 cmdd bytes.fromhex(cmd) ser.write(cmdd) def hide_label_and_send_cmd(label,serNone): 隐藏报警标签并停止报警 label.setVisible(False) if ser: sendCmdToDevice(BUZZ_CMD_CLOSE, ser)2字符标准化解决易混淆字符问题def set_bing(set_a): 替换工业场景易混淆字符0→O、s→5、I→1等 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 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}$) # 正则24-7位纯数字 pattern_at_least_two_digits re.compile(r^\d{4,7}$) for part in parts: if pattern_alphanumeric.match(part) or pattern_at_least_two_digits.match(part): aa.append(part) return aa3字符区域面积计算筛选最大字符区域def are(i): 计算OCR识别字符的包围框面积 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)) width (width_A width_B) / 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)) height (height_A height_B) / 2 area width * height return area4. 摄像头画面采集与 OCR 识别def _queryFrame(self): 定时器回调采集摄像头画面并触发OCR try: if not self.camera3.grab(): print(USB摄像头无画面) self.camera3.release() self.close() else: # 读取三路摄像头画面 self.frame1: ndarray self.camera1.get_image() self.frame2: ndarray self.camera2.get_image() ret3, self.frame3 self.camera3.read() if ret3: # 调整画面尺寸并显示到UI self.frame11 cv2.resize(self.frame1, (640, 480)) self.frame22 cv2.resize(self.frame2, (640, 480)) self.frame33 cv2.resize(self.frame3, (640, 480)) # 转换为Qt图像格式并显示 qimage cv2.cvtColor(self.frame11.copy(), cv2.COLOR_BGR2RGB) qimage QtGui.QImage(qimage.data, qimage.shape[1], qimage.shape[0], QtGui.QImage.Format_RGB888) self.label.setPixmap(QtGui.QPixmap.fromImage(qimage)) qimage2 cv2.cvtColor(self.frame22.copy(), cv2.COLOR_BGR2RGB) qimage2 QtGui.QImage(qimage2.data, qimage2.shape[1], qimage2.shape[0], QtGui.QImage.Format_RGB888) self.label_3.setPixmap(QtGui.QPixmap.fromImage(qimage2)) qimage3 cv2.cvtColor(self.frame33.copy(), cv2.COLOR_BGR2RGB) qimage3 QtGui.QImage(qimage3.data, qimage3.shape[1], qimage3.shape[0], QtGui.QImage.Format_RGB888) self.label_2.setPixmap(QtGui.QPixmap.fromImage(qimage3)) # 帧间隔触发OCR降低计算量 self.frame_counter 1 if self.frame_counter % 5 0: self._performOCR1() # 摄像头1 OCR if self.frame_counter % 3 0: self._performOCR2() # 摄像头2 OCR self._performOCR3() # 摄像头3 OCR except Exception as e: print(f画面采集异常{e}) # 装饰器指定调用次数后执行数据处理 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: w2set(self) return result wrapper.count 0 return wrapper return decorator execute_after_n_calls(3, lambda self: self.w2set1()) def _performOCR1(self): 摄像头1 OCR识别 result ocr2.ocr(self.frame1, clsFalse) if result and not None in result: try: for i in result[0]: mianji are(i) ma process_string(i[1][0]) # 过滤条件置信度0.92、有效型号、位置范围、面积1000 if i[1][1] 0.92 and ma and 320 i[0][0][0] 1060 and mianji 1000: self.list_zong1.extend(ma) except: pass def w2set1(self): 摄像头1识别结果处理异常判断报警 if self.list_zong1: try: # 筛选出现次数1的识别结果 for i in self.list_zong1: if self.list_zong1.count(i) 1: self.set_zong1.add(i) set_len1 set_bing(self.set_zong1) # 字符标准化 self.list1.extend(list(set_len1)) self.daan1 most_common_element(self.list1) # 取出现次数最多的作为基准 # 10秒刷新一次基准列表 if self.frame_counter % 16 0: self.list1.clear() # 显示识别结果 text ,.join(set_len1) 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: # 异常处理保存画面、显示报警标签、触发声光报警 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(rD:\MVS\MVS\Development\Samples\Python\shiyan\baojing\镜头一, f{nam}-{self.daan1}.jpg) cv2.imencode(.jpg, tu1)[1].tofile(path) self.label_4.setVisible(True) # 显示报警标签 sendCmdToDevice(LIGHT, self.ser1) # 触发闪光报警 # 5秒后关闭报警 QtCore.QTimer.singleShot(5000, lambda: hide_label_and_send_cmd(self.label_4,self.ser1)) self.frame_counter 0 except Exception as e: print(f摄像头1处理异常{e}) finally: self.list_zong1.clear() self.set_zong1.clear()5. 系统运行入口if __name__ __main__: app QtWidgets.QApplication(sys.argv) window PyQtMainEntry() window.show() sys.exit(app.exec_())四、系统亮点多摄像头适配同时支持工业网口相机和 USB 摄像头满足不同场景需求字符鲁棒识别针对工业场景易混淆字符0/O、1/I/L、5/S 等做专项替换提升识别准确率性能优化通过帧间隔 OCR、字符面积筛选、出现次数统计降低计算量同时提升识别稳定性异常处理完善异常时自动保存取证画面、触发声光报警5 秒后自动复位无需人工干预线程安全后台线程管理摄像头资源避免 UI 卡顿和资源泄漏。五、扩展方向模型优化针对特定产品型号定制 PaddleOCR 训练数据集进一步提升识别准确率远程监控增加网络推送功能将异常画面和报警信息推送到手机 / 电脑端数据统计增加数据库存储功能统计异常型号出现频次、报警时间等辅助生产分析自适应调节根据环境光照自动调整相机曝光参数提升复杂环境下的识别稳定性。该系统已在实际工业检测场景中落地应用能够有效替代人工肉眼识别提升检测效率和准确率降低漏检、错检率。核心代码具备良好的可扩展性可根据不同工业场景的需求快速适配调整。