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

如何基于OpenCV改进简单交通灯检测代码实现全图检测?

问题:OpenCV简单交通灯检测仅检测图像上部,如何修改?

我希望在Python中借助OpenCV实现简单的交通灯检测算法。当然,若要获得高精度应使用预训练深度学习模型,但目前我仅需最简单的非全面方案。由于交通灯存在红、绿、黄三种颜色,我找到了一份实现三色检测的代码。

我清楚这并非精准方法,仅用于自学。我已在一段视频上测试了该代码,视频的截取帧如下:
视频截取帧

运行代码后得到如下结果图:
检测结果图

可以看到,图像下部被忽略,仅检测了上部区域。


我该如何调整或修改代码,使其能检测整张图像中的真实交通灯?是否需要将图像调整为更低分辨率?或是采用其他方法?我考虑过调整帧大小,但想先听取意见。我认为核心问题出在轮廓坐标定位的代码部分,该如何修改?


测试代码

import numpy as np
import cv2
import warnings
warnings.filterwarnings("ignore")
# Capturing video through webcam
live_video = cv2.VideoCapture("traffic_light.mp4")
# Start a while loop
while (1):
    # Reading the video from the
    # webcam in image frames
    _, imageFrame = live_video .read()

    # Convert the imageFrame in
    # BGR(RGB color space) to
    # HSV(hue-saturation-value)
    # color space
    hsvFrame = cv2.cvtColor(imageFrame, cv2.COLOR_BGR2HSV)

    # Set range for red color and
    # define mask
    red_lower = np.array([136, 87, 111], np.uint8)
    red_upper = np.array([180, 255, 255], np.uint8)
    red_mask = cv2.inRange(hsvFrame, red_lower, red_upper)

    # Set range for green color and
    # define mask
    green_lower = np.array([25, 52, 72], np.uint8)
    green_upper = np.array([102, 255, 255], np.uint8)
    green_mask = cv2.inRange(hsvFrame, green_lower, green_upper)

    # Set range for blue color and
    # define mask
    blue_lower = np.array([94, 80, 2], np.uint8)
    blue_upper = np.array([120, 255, 255], np.uint8)
    blue_mask = cv2.inRange(hsvFrame, blue_lower, blue_upper)

    # Morphological Transform, Dilation
    # for each color and bitwise_and operator
    # between imageFrame and mask determines
    # to detect only that particular color
    kernal = np.ones((5, 5), "uint8")

    # For red color
    red_mask = cv2.dilate(red_mask, kernal)
    res_red = cv2.bitwise_and(imageFrame, imageFrame,
                              mask=red_mask)

    # For green color
    green_mask = cv2.dilate(green_mask, kernal)
    res_green = cv2.bitwise_and(imageFrame, imageFrame,
                                mask=green_mask)

    # For blue color
    blue_mask = cv2.dilate(blue_mask, kernal)
    res_blue = cv2.bitwise_and(imageFrame, imageFrame,
                               mask=blue_mask)

    # Creating contour to track red color
    contours, hierarchy = cv2.findContours(red_mask,
                                           cv2.RETR_TREE,
                                           cv2.CHAIN_APPROX_SIMPLE)

    for pic, contour in enumerate(contours):
        area = cv2.contourArea(contour)
        if (area > 300):
            x, y, w, h = cv2.boundingRect(contour)
            imageFrame = cv2.rectangle(imageFrame, (x, y),
                                       (x + w, y + h),
                                       (0, 0, 255), 2)

            cv2.putText(imageFrame, "Red Colour", (x, y),
                        cv2.FONT_HERSHEY_SIMPLEX, 1.0,
                        (0, 0, 255))

            # Creating contour to track green color
    contours, hierarchy = cv2.findContours(green_mask,
                                           cv2.RETR_TREE,
                                           cv2.CHAIN_APPROX_SIMPLE)

    for pic, contour in enumerate(contours):
        area = cv2.contourArea(contour)
        if (area > 300):
            x, y, w, h = cv2.boundingRect(contour)
            imageFrame = cv2.rectangle(imageFrame, (x, y),
                                       (x + w, y + h),
                                       (0, 255, 0), 2)

            cv2.putText(imageFrame, "Green Colour", (x, y),
                        cv2.FONT_HERSHEY_SIMPLEX,
                        1.0, (0, 255, 0))

    # Creating contour to track blue color
    contours, hierarchy = cv2.findContours(blue_mask,
                                           cv2.RETR_TREE,
                                           cv2.CHAIN_APPROX_SIMPLE)
    for pic, contour in enumerate(contours):
        area = cv2.contourArea(contour)
        if (area > 300):
            x, y, w, h = cv2.boundingRect(contour)
            imageFrame = cv2.rectangle(imageFrame, (x, y),
                                       (x + w, y + h),
                                       (255, 0, 0), 2)

            cv2.putText(imageFrame, "Blue Colour", (x, y),
                        cv2.FONT_HERSHEY_SIMPLEX,
                        1.0, (255, 0, 0))

    # Program Termination
    cv2.imshow("Multiple Color Detection in Real-TIme", imageFrame)
    if cv2.waitKey(10) & 0xFF == ord('q'):
        live_video  .release()
        cv2.destroyAllWindows()
        break

解决方案

1. 核心问题分析

下部区域检测不到的问题,并非轮廓坐标定位的问题,根源在于:

  • 原代码未覆盖交通灯的黄色检测
  • 预设的HSV颜色范围与视频中下部交通灯的实际颜色不匹配
  • 轮廓面积阈值300对画面中尺寸更小的下部交通灯来说过高

2. 具体修改步骤

(1)添加黄色检测逻辑

在绿色检测代码后插入黄色的HSV范围定义与形态学处理:

# Set range for yellow color and define mask
yellow_lower = np.array([20, 100, 100], np.uint8)
yellow_upper = np.array([30, 255, 255], np.uint8)
yellow_mask = cv2.inRange(hsvFrame, yellow_lower, yellow_upper)

# Morphological Dilation for yellow
yellow_mask = cv2.dilate(yellow_mask, kernal)
res_yellow = cv2.bitwise_and(imageFrame, imageFrame, mask=yellow_mask)

再添加黄色的轮廓检测与绘制代码:

# Creating contour to track yellow color
contours, hierarchy = cv2.findContours(yellow_mask,
                                       cv2.RETR_TREE,
                                       cv2.CHAIN_APPROX_SIMPLE)
for pic, contour in enumerate(contours):
    area = cv2.contourArea(contour)
    if (area > 100):  # 降低阈值适配小尺寸交通灯
        x, y, w, h = cv2.boundingRect(contour)
        imageFrame = cv2.rectangle(imageFrame, (x, y),
                                   (x + w, y + h),
                                   (0, 255, 255), 2)
        cv2.putText(imageFrame, "Yellow Colour", (x, y),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5,
                    (0, 255, 255))

(2)调整绿色HSV范围适配实际场景

原绿色范围过宽易误检,修改为更精准的区间:

green_lower = np.array([40, 40, 40], np.uint8)
green_upper = np.array([80, 255, 255], np.uint8)

(3)降低轮廓面积阈值

将所有area > 300的判断改为area > 100(可根据视频实际情况微调),确保小尺寸交通灯的轮廓能被捕捉到。

(4)可选:简化代码结构

将颜色检测与轮廓绘制封装为函数,避免重复代码:

def detect_color(imageFrame, mask, color, label, area_threshold=100, font_scale=0.5):
    contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    for contour in contours:
        area = cv2.contourArea(contour)
        if area > area_threshold:
            x, y, w, h = cv2.boundingRect(contour)
            cv2.rectangle(imageFrame, (x, y), (x+w, y+h), color, 2)
            cv2.putText(imageFrame, label, (x, y), cv2.FONT_HERSHEY_SIMPLEX, font_scale, color)
    return imageFrame

在主循环中调用该函数:

imageFrame = detect_color(imageFrame, red_mask, (0,0,255), "Red Colour")
imageFrame = detect_color(imageFrame, green_mask, (0,255,0), "Green Colour")
imageFrame = detect_color(imageFrame, blue_mask, (255,0,0), "Blue Colour")
imageFrame = detect_color(imageFrame, yellow_mask, (0,255,255), "Yellow Colour")

3. 其他优化建议

  • 无需降低分辨率:低分辨率会丢失小交通灯的细节,保持原分辨率即可。
  • 添加高斯模糊预处理:减少噪声干扰,放在cv2.cvtColor之前:
    imageFrame = cv2.GaussianBlur(imageFrame, (5,5), 0)
    
  • 替换膨胀为开运算:过滤小噪声点,比如:
    red_mask = cv2.morphologyEx(red_mask, cv2.MORPH_OPEN, kernal)
    

内容的提问来源于stack exchange,提问作者AI researcher

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 22:10:42