(5-5-2)交通信号识别:基于深度学习的智能交通违章检测系统

5.4.5  车牌提取

在这一步中,将介绍如何使用数字图像处理技术从夜间交通帧中提取车牌,并确保在不同条件下的稳定性和准确性。定义函数 extract_license_plate,用于从给定的图像帧中提取车牌。首先,它将图像转换为灰度图并应用CLAHE直方图均衡化以增强对比度。然后,通过腐蚀操作去除噪声,并找到非黑色像素的边界框。接下来,它在边界框内使用Haar级联分类器检测潜在的车牌位置,并在原始图像上绘制矩形来标记检测到的车牌位置。最后,它将检测到的车牌裁剪并存储在列表中,同时返回带有标记车牌位置的原始图像。

def extract_license_plate(frame, mask_line):    
    # 将图像转换为灰度图(Haar级联通常在灰度图像上进行训练)
    gray = cv2.cvtColor(mask_line, cv2.COLOR_BGR2GRAY)
    
    # 应用CLAHE来均衡直方图
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
    gray = clahe.apply(gray)
    
    # 使用2x2的核来腐蚀图像以去除噪声
    kernel = np.ones((2, 2), np.uint8)
    gray = cv2.erode(gray, kernel, iterations=1)

    # 查找非黑色像素的边界框
    non_black_points = cv2.findNonZero(gray)
    x, y, w, h = cv2.boundingRect(non_black_points)

    # 计算边界框的新宽度,右侧排除30%
    w = int(w * 0.7)

    # 将图像裁剪到边界框
    cropped_gray = gray[y:y+h, x:x+w]

    # 在图像中检测车牌(这会返回一个矩形列表)
    license_plates = license_plate_cascade.detectMultiScale(cropped_gray, scaleFactor=1.07, minNeighbors=15, minSize=(20, 20))

    # 用于保存裁剪的车牌图像的列表
    license_plate_images = []

    # 遍历车牌
    for (x_plate, y_plate, w_plate, h_plate) in license_plates:
        # 在原始帧中的车牌周围绘制矩形(这里需要原始坐标)
        cv2.rectangle(frame, (x_plate + x, y_plate + y), (x_plate + x + w_plate, y_plate + y + h_plate), (0, 255, 0), 3)
    
        # 裁剪车牌并将其附加到列表中(这里x_plate和y_plate相对于cropped_gray)
        license_plate_image = cropped_gray[y_plate:y_plate+h_plate, x_plate:x_plate+w_plate]
        license_plate_images.append(license_plate_image)

    return frame, license_plate_images

上述代码的实现流程如下所示:

(1)预处理:从基本的预处理步骤开始,包括将图像转换为灰度图像以使用单通道强度值,并应用CLAHE来均衡直方图,从而增强图像对比度。此外,使用腐蚀来最小化噪声,为更准确的检测铺平道路。

(2)隔离和裁剪感兴趣区域(ROI):在前面步骤实现的掩码上进行处理,其中线上方的像素被涂成黑色,代码识别了表示停车线下方道路的非黑色像素。计算该区域的边界框,然后裁剪它,另外还稍微排除了属于对向车道的左侧一部分。这个集中的裁剪区域成为车牌检测的主要ROI,因为__Haar级联分类器__在较小、定义明确的ROI上执行效果更好。

(3)Haar级联分类器:将裁剪的灰度图像传递给Haar级联分类器,这是一种经典的非深度学习方法,擅长于模式识别。该分类器经过预训练以识别车牌模式,并以矩形的形式返回潜在车牌的位置。

(4)提取车牌并最终输出:将检测到的车牌在原始帧上用矩形标记。此外,还从灰度区域中裁剪出潜在的车牌,并将其存储在列表中。该函数通过返回带有车牌边界的原始帧和潜在检测到的车牌来结束。

函数extract_license_plate 是一个简化且组织良好的函数,对于需要车牌识别的应用程序至关重要。它利用经典的计算机视觉技术有效地提取所需的有价值信息。

5.4.6  车牌号码的文本识别

在这一步中,将使用 PyTesseract OCR对提取的车牌图像进行文本识别处理,以获取具体的车牌号码。定义一个名为apply_ocr_to_image的函数,该函数旨在利用PyTesseract OCR从给定的车牌图像中识别和提取文本信息(如数字和字符)。

def apply_ocr_to_image(license_plate_image):    
    # 图像阈值化
    _, img = cv2.threshold(license_plate_image, 120, 255, cv2.THRESH_BINARY)
    # 将OpenCV图像格式转换为PIL图像格式以供Pytesseract使用
    pil_img = Image.fromarray(img)
    # 使用Pytesseract从图像中提取文本
    full_text = pytesseract.image_to_string(pil_img, config='--psm 6')
    return full_text.strip()  # 移除识别文本两端的额外空白

上述代码的实现流程如下所示:

(1)图像阈值化:对输入的车牌图像进行阈值化处理,将其转换为二进制格式,增强文本与背景的对比度。

(2)格式转换:将阈值化处理后的图像,初始为OpenCV格式,转换为PyTesseract所需的PIL图像格式,以便进行OCR处理。

(3)使用经典OCR提取文本:利用PyTesseract的image_to_string函数从转换后的图像中提取文本。使用OCR配置--psm 6处理典型车牌上稀疏背景的稀疏文本。

(4)文本清理:在文本提取后,利用strip()方法移除识别文本开头或结尾的任何多余空白,以确保返回的文本整洁紧凑。

函数apply_ocr_to_image有助于高效地从车牌图像中提取文本信息,充分利用PyTesseract提供的传统OCR技术功能。

5.4.7  显示处罚车牌

这一步将介绍如何将违规行为的车牌号码动态显示在视频帧上,以便实现可视化展示功能。下面的draw_penalized_text函数被巧妙地用于在视频帧上显示受罚的车牌,这种动态显示效果确保了每个被罚款的车牌按其识别顺序显示。无论视频源是来自离线录制还是实时监控,被罚车牌的列表都会动态更新在车辆监控屏幕上。

def draw_penalized_text(frame, penalized_plates):
    # 在视频帧上绘制受罚车牌的文本
    for idx, plate in enumerate(penalized_plates):
        # 设置文本的位置和样式
        text = f"Penalized Plate: {plate}"
        font = cv2.FONT_HERSHEY_SIMPLEX
        font_scale = 1
        font_thickness = 2
        text_color = (0, 0, 255)  # 红色
        bg_color = (255, 255, 255)  # 白色

        # 获取文本的宽度和高度
        (text_width, text_height), _ = cv2.getTextSize(text, font, font_scale, font_thickness)
        
        # 计算文本位置
        text_x = int((frame.shape[1] - text_width) / 2)
        text_y = 40 + idx * (text_height + 10)  # 每个车牌的文本位置间隔
        
        # 绘制文本的背景框
        bg_rect_start = (text_x, text_y - text_height)
        bg_rect_end = (text_x + text_width, text_y + int(text_height * 0.25))
        cv2.rectangle(frame, bg_rect_start, bg_rect_end, bg_color, cv2.FILLED)
        
        # 在视频帧上绘制文本
        cv2.putText(frame, text, (text_x, text_y), font, font_scale, text_color, font_thickness)
    
    return frame

通过函数draw_penalized_text,使用者可以在车辆监控屏幕上实时跟踪和查看受罚的车牌,使其成为任何车辆监控或交通管理系统中不可或缺的工具。

5.4.8  将违规车牌记录到 MySQL 中

在这一步中,将产生违规行为的车牌号码记录到 MySQL 数据库中,以便进一步处理和管理。在这一部分将提供一些函数,这些函数共同与MySQL数据库集成,确保所有被罚款的车牌都被有效记录。这些函数提供了根据需要创建、更新、查看和清除车牌数据的能力,共同实现了对车牌违规行为的高效管理,确保了数据的准确性和易于访问。鉴于某些函数是可选的,开发人员可以根据应用程序的需求选择部署它们中觉得必要的部分。

(1)数据库和表的初始化

函数create_database_and_table()用于创建一个新的 MySQL 数据库,并在该数据库中创建一个名为 license_plates 的表格,用于记录车牌号和相应的违规计数。

def create_database_and_table(host, user, password, database):
try:
    # 创建一个连接
    connection = mysql.connector.connect(
        host=host,
        user=user,
        password=password
    )
    
    if connection.is_connected():
        # 创建一个新的数据库游标
        cursor = connection.cursor()

        # 使用提供的名称创建一个新数据库
        cursor.execute(f"CREATE DATABASE IF NOT EXISTS {database}")
        print(f"数据库 {database} 创建成功!")

        # 使用新创建的数据库
        cursor.execute(f"USE {database}")

        # 创建一个新的表
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS license_plates (
                id INT AUTO_INCREMENT PRIMARY KEY,
                plate_number VARCHAR(255) NOT NULL UNIQUE,
                violation_count INT DEFAULT 1
            )
        """)
        print("表格创建成功!")

        cursor.close()

except Error as e:
    print("连接到 MySQL 时出错", e)

finally:
    if connection.is_connected():
        connection.close()

对上述代码的具体说明如下所示:

  1. 使用提供的凭据建立与MySQL的连接。
  2. 如果数据库不存在,则创建一个新的数据库,并选择它进行后续操作。
  3. 构建一个名为license_plates的表,其中包含每个车牌及其相应违规计数的记录。
  4. 需要注意的是,一旦数据库设置好了,这个函数可能是可选的,因为没有必要重复创建结构。

(2)函数update_database_with_violation()用于更新 MySQL 数据库中的违规记录。首先,连接到指定的 MySQL 数据库。然后,检查车牌号是否已存在于数据库中。如果车牌号已存在,则将其对应的违规计数加一;如果不存在,则添加一个新的记录,违规计数默认为1。

def update_database_with_violation(plate_number, host, user, password, database):
    try:
        connection = mysql.connector.connect(
            host=host,
            user=user,
            password=password,
            database=database
        )
        
        if connection.is_connected():
            cursor = connection.cursor()

            # 检查车牌是否已存在于表中
            cursor.execute(f"SELECT violation_count FROM license_plates WHERE plate_number='{plate_number}'")
            result = cursor.fetchone()
            
            if result:
                # 如果车牌已存在,则将违规次数加1
                cursor.execute(f"UPDATE license_plates SET violation_count=violation_count+1 WHERE plate_number='{plate_number}'")
            else:
                # 如果车牌不存在,则插入新记录,默认违规次数为1
                cursor.execute(f"INSERT INTO license_plates (plate_number) VALUES ('{plate_number}')")
            
            connection.commit()
            cursor.close()

    except Error as e:
        print("连接到MySQL时出错", e)

    finally:
        if connection.is_connected():
            connection.close()

(3)下面的代码定义了函数 print_all_violations,用于连接到指定的 MySQL 数据库并打印其中记录的所有交通违规信息。首先连接到数据库,然后执行查询以检索所有车牌号和对应的违规次数,并按违规次数降序排序。最后,它将结果打印出来,显示车牌号和违规次数。

def print_all_violations(host, user, password, database):
    try:
        connection = mysql.connector.connect(
            host=host,
            user=user,
            password=password,
            database=database
        )
        
        if connection.is_connected():
            cursor = connection.cursor()

            # 从数据库中提取所有违规记录
            cursor.execute("SELECT plate_number, violation_count FROM license_plates ORDER BY violation_count DESC")
            result = cursor.fetchall()
            
            print("\n")
            print("-"*66)
            print("\n数据库中所有已注册的交通违规记录:\n")
            for record in result:
                print(f"车牌号码:{record[0]}, 违规次数:{record[1]}")
            
            cursor.close()

    except Error as e:
        print("连接到MySQL时出错", e)

    finally:
        if connection.is_connected():
            connection.close()

(4)函数 clear_license_plates用于连接到指定的 MySQL 数据库,并清空其中存储的所有车牌信息。首先连接到数据库,然后执行一个 SQL 查询,删除 license_plates 表中的所有记录。最后,函数clear_license_plates提交事务并关闭数据库连接。

def clear_license_plates(host, user, password, database):

# 尝试连接到 MySQL 数据库并清空所有车牌信息
try:
    connection = mysql.connector.connect(
        host=host,
        user=user,
        password=password,
        database=database
    )
    
    # 如果连接成功
    if connection.is_connected():
        cursor = connection.cursor()

        # 从表中删除所有记录
        cursor.execute("DELETE FROM license_plates")

        # 提交事务
        connection.commit()
        cursor.close()

# 如果连接过程中出现错误,则打印错误信息
except Error as e:
    print("连接到 MySQL 数据库时出错", e)

# 最终无论如何都要确保关闭数据库连接
finally:
    if connection.is_connected():
        connection.close()

5.4.9  运行交通违规监控系统

这一步将展示如何执行整个交通违规监控系统,包括实时监测、记录违规行为并进行必要的处罚。

(1)定义函数main,该函数封装了从视频流中检测车牌违规的整个流程。函数main基于一段交通的离线视频,逐帧读取视频,处理每一帧以检测任何交通违规行为。

def main():
    # 确保数据库和表存在
    create_database_and_table(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME)

    # 清除上次运行的车牌信息。(如果需要可以注释掉这行!)
    clear_license_plates(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME)

    # 打开视频文件
    vid = cv2.VideoCapture('traffic_video_modified.mp4')
    
    # 创建检测器对象
    detector = LineDetector()

    # 遍历视频中的每一帧
    while True:
        # 读取帧
        ret, frame = vid.read()
        
        # 如果没有返回帧,则退出循环
        if not ret:
            break

        # 假设rect是交通信号灯所在的矩形区域
        rect = (1700, 40, 100, 250) 
        
        # 检测交通信号灯的颜色
        frame, color = detect_traffic_light_color(frame, rect)
        
        # 检测白色线条
        frame, mask_line = detector.detect_white_line(frame, color)
        
        # 如果信号灯为红色,则处理该帧
        if color == 'red':
            # 提取车牌
            frame, license_plate_images = extract_license_plate(frame, mask_line)
            
            # 处理每个检测到的车牌
            for license_plate_image in license_plate_images:
                # 对车牌图像应用OCR
                text = apply_ocr_to_image(license_plate_image)
                
                # 如果文本不为空且匹配了预定义模式,并且不在已罚款文本列表中,则添加到列表中
                if text is not None and re.match("^[A-Z]{2}\s[0-9]{3,4}$", text) and text not in penalized_texts:
                    penalized_texts.append(text)
                    print(f"\n罚款车牌: {text}")

                    # 绘制车牌图像
                    plt.figure()
                    plt.imshow(license_plate_image, cmap='gray')
                    plt.axis('off')
                    plt.show()
                    
                    # 更新数据库中的车牌违规记录
                    update_database_with_violation(text, DB_HOST, DB_USER, DB_PASSWORD, DB_NAME)
        
        # 如果有罚款车牌,则在帧上绘制
        if penalized_texts:
            draw_penalized_text(frame)

        # 显示帧 
        cv2.imshow('frame', frame)

        # 如果按下ESC键,则退出(在支持GUI的本地系统上运行时取消下面一行的注释)
        if cv2.waitKey(1) == 27:
            break

    # 释放视频
    vid.release()
    
    # 关闭所有OpenCV窗口(在支持GUI的本地系统上运行时取消下面一行的注释)
    cv2.destroyAllWindows()

    # 打印数据库中的所有违规记录
    print_all_violations(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME)

在上述代码中,函数main实现了一个完整的交通违规检测系统,包括车牌识别、违规记录、数据库更新和显示输出等功能。具体实现流程如下所示。

  1. 确保数据库和表的存在。
  2. 清除上次运行时记录的车牌信息。
  3. 打开视频文件并创建检测器对象。
  4. 逐帧读取视频,检测交通信号灯颜色和白线,并在红灯状态下处理帧。
  5. 提取检测到的车牌并应用光学字符识别(OCR)。
  6. 更新违规车牌信息到数据库,并在匹配条件下添加到已罚款车牌列表。
  7. 在帧上绘制已罚款车牌信息。
  8. 显示处理后的帧,并按 ESC 键退出。
  9. 打印输出数据库中保存的所有违规记录。

(2)在本项目的车牌检测机制中,我们使用了一个 Haar 级联分类器。在调试运行,首先从 GitHub 仓库下载我们预先训练好的模型 haarcascade_russian_plate_number.xml。这是一个专门用于检测车牌的模型,对于在视频帧中识别车牌至关重要。通过初始化 Haar 级联对象,确保我们的程序不会因为多次初始化而遭受重复的延迟。

在这之后,初始化了一个名为 penalized_texts 的列表。这个列表作为一个存储库,记录了交通违规中涉及的车牌文本。对这个列表的全局定义确保了主函数和 draw_penalized_text 函数都可以访问和更新其内容而不会出现问题。

# 从GitHub存储库下载经过训练的 Haar Cascade 模型
url = "https://raw.githubusercontent.com/FarzadNekouee/Traffic-Violation-Detection/master/haarcascade_russian_plate_number.xml"
response = requests.get(url)

with open('haarcascade_russian_plate_number.xml', 'wb') as file:
    file.write(response.content)

# 加载经过训练的 Haar Cascade 模型
license_plate_cascade = cv2.CascadeClassifier('haarcascade_russian_plate_number.xml')

# 创建一个列表,用于存储唯一的受罚车牌文本信息
penalized_texts = []

(3)通过如下代码启动主程序,一旦启动,函数main就会开始其复杂的过程,识别视频中的车牌违规行为,并详细记录任何违规行为。

if __name__ == "__main__":
     main()

执行效果如图5-8所示。

图5-8  执行效果

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农三叔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值