如何在不中断摄像头流时将YOLOv4检测后的easyOCR移出循环?
分离车牌检测与OCR识别,实现无卡顿摄像头流
核心思路
将耗时的OCR识别任务放到独立子线程中执行,主线程专注于摄像头帧读取和YOLO车牌检测,通过线程安全队列传递待识别的车牌图像,让两个任务并行运行,互不阻塞摄像头流。
具体实现步骤
1. 导入必要模块
在原有依赖基础上,新增线程和队列相关模块:
import cv2 import numpy as np import easyocr from queue import Queue import threading
2. 初始化队列与OCR线程
创建线程安全队列用于传递图像,定义OCR子线程的执行逻辑:
# 初始化队列,限制最大长度避免内存溢出 ocr_queue = Queue(maxsize=10) # 全局初始化easyOCR阅读器(避免循环内重复加载模型) reader = easyocr.Reader(['ch_sim', 'en']) # 根据需求调整语言 def ocr_worker(): """OCR子线程:持续从队列取图像执行识别""" while True: # 队列为空时阻塞等待,获取待识别图像 cropped_img = ocr_queue.get() if cropped_img is None: # 用None作为线程终止信号 break # 执行OCR识别逻辑 result = reader.readtext(cropped_img) text = '' for item in result: text += item[1] + ' ' spliced = text.strip().upper() # 替换你原有的remove()逻辑 print(f"识别结果:{spliced}") # 标记当前任务完成,更新队列计数 ocr_queue.task_done()
3. 修改主线程检测循环
移除循环内直接调用OCR的代码,改为将裁剪后的车牌图像放入队列,主线程继续处理下一帧:
# 加载YOLOv4模型(替换为你的模型路径) classes = ["license_plate"] net = cv2.dnn.readNet("yolov4-obj.weights", "yolov4-obj.cfg") # 启动OCR子线程(设置daemon=True,主线程结束时自动终止子线程) ocr_thread = threading.Thread(target=ocr_worker, daemon=True) ocr_thread.start() # 摄像头读取循环 cap = cv2.VideoCapture(0) # 替换为摄像头索引或视频路径 while True: ret, img = cap.read() if not ret: break hight, width, _ = img.shape blob = cv2.dnn.blobFromImage(img, 1 / 255, (416, 416), (0, 0, 0), swapRB=True, crop=False) net.setInput(blob) output_layers_name = net.getUnconnectedOutLayersNames() layerOutputs = net.forward(output_layers_name) boxes = [] confidences = [] class_ids = [] # 单次YOLO检测(优化原代码中重复检测的冗余逻辑) for output in layerOutputs: for detection in output: score = detection[5:] class_id = np.argmax(score) confidence = score[class_id] if confidence > 0.5: center_x = int(detection[0] * width) center_y = int(detection[1] * hight) w = int(detection[2] * width) h = int(detection[3] * hight) x = int(center_x - w / 2) y = int(center_y - h / 2) boxes.append([x, y, w, h]) confidences.append(float(confidence)) class_ids.append(class_id) indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.8, 0.4) font = cv2.FONT_HERSHEY_PLAIN colors = np.random.uniform(0, 255, size=(len(boxes), 3)) if len(indexes) > 0: for i in indexes.flatten(): x, y, w, h = boxes[i] label = str(classes[class_ids[i]]) confidence = str(round(confidences[i], 2)) color = colors[i] cv2.rectangle(img, (x, y), (x + w, y + h), color, 2) cv2.putText(img, f"{label} {confidence}", (x, y - 10), font, 2, color, 2) detected_image = img[y:y+h, x:x+w] cv2.imshow('detection', detected_image) # 将图像副本放入队列(避免原图像被主线程后续帧覆盖) if not ocr_queue.full(): ocr_queue.put(detected_image.copy()) cv2.imshow('Camera Stream', img) if cv2.waitKey(1) & 0xFF == ord('q'): break # 发送终止信号,等待OCR线程优雅退出 ocr_queue.put(None) ocr_thread.join() # 释放资源 cap.release() cv2.destroyAllWindows()
关键细节说明
- 线程安全队列:
Queue类自带锁机制,put()和get()方法会自动处理多线程竞争问题,无需手动加锁。 - 图像副本传递:使用
detected_image.copy()传递图像,因为OpenCV图像是可变numpy数组,直接传引用会导致子线程拿到被主线程修改的图像。 - 队列长度限制:
maxsize=10防止队列积累过多未识别图像导致内存占用过高,队列满时put()会自动阻塞,也可改用put_nowait()丢弃新图像(根据需求选择)。
内容的提问来源于stack exchange,提问作者mandebo
相关产品推荐
相关产品推荐

