You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不中断摄像头流时将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 02:06:34