你提出的需求非常明确且实用:
> ✅ **加强蓝底白字车牌的识别能力**
> ✅ **支持视频文件识别**
> ✅ **支持调用摄像头实时识别**
---
## ✅ 解决方案
我们将基于前一版修复代码进行 **全面升级**,实现一个 **工业级可用的车牌识别系统**。
### 🔧 主要优化点:
| 功能 | 实现方式 |
|------|---------|
| 🎯 提升蓝底白字识别率 | 添加专用预处理:`增强蓝色通道 + 白色字符二值化` |
| 📹 支持视频播放 | 使用 `QTimer` 定时读取帧 |
| 📷 支持摄像头 | `cv2.VideoCapture(0)` 调用默认摄像头 |
| 🔄 自动切换模式 | 智能判断输入源(图片/视频/摄像头) |
---
## ✅ 完整可运行代码(已集成全部功能)
```python
# -*- coding: utf-8 -*-
"""
🚗 车牌识别系统(终极增强版)
✅ 支持:图片 / 视频 / 摄像头
✅ 优化:蓝底白字车牌识别(专为普通车牌设计)
✅ 功能:实时检测 + 显示框 + 格式校验 + 结果输出
"""
import sys
import cv2
import easyocr
import numpy as np
import re
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QFileDialog, QVBoxLayout, QWidget, \
QHBoxLayout, QFrame, QComboBox
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtCore import Qt, QTimer
class LicensePlateRecognition(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("🚗 车牌识别系统(增强版)")
self.setGeometry(100, 100, 1200, 750)
# 初始化 EasyOCR
try:
self.reader = easyocr.Reader(['ch_sim', 'en'], gpu=False)
except Exception as e:
print("EasyOCR 初始化失败,请安装:pip install easyocr")
sys.exit()
# 媒体控制变量
self.cap = None
self.timer = QTimer()
self.timer.timeout.connect(self.update_frame)
self.current_mode = "image" # image, video, camera
self.init_ui()
def init_ui(self):
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QHBoxLayout(central_widget)
# 左侧控制面板
control_layout = QVBoxLayout()
# 模式选择下拉框
self.mode_combo = QComboBox()
self.mode_combo.addItems(["📷 图片识别", "🎥 视频识别", "📹 摄像头实时识别"])
self.mode_combo.currentIndexChanged.connect(self.on_mode_change)
control_layout.addWidget(self.mode_combo)
# 加载按钮
self.load_btn = QPushButton("📁 选择文件")
self.load_btn.clicked.connect(self.load_media)
self.load_btn.setStyleSheet("font-size: 16px; padding: 12px;")
control_layout.addWidget(self.load_btn)
# 结果显示区
self.result_label = QLabel("🔍 识别结果:等待输入...")
self.result_label.setWordWrap(True)
self.result_label.setStyleSheet("""
QLabel {
background-color: #f0f8ff;
border: 1px solid #ccc;
padding: 15px;
font-size: 16px;
min-height: 80px;
}
""")
control_layout.addWidget(self.result_label)
control_layout.addStretch()
# 右侧图像显示
self.video_label = QLabel("📷 图像将显示在此处")
self.video_label.setAlignment(Qt.AlignCenter)
self.video_label.setFrameShape(QFrame.Box)
self.video_label.setStyleSheet("QLabel { background-color: #000; color: white; font-size: 18px; }")
# 组合布局
layout.addLayout(control_layout, 1)
layout.addWidget(self.video_label, 3)
def on_mode_change(self):
"""切换模式时停止当前任务"""
self.stop_capture()
def load_media(self):
"""根据当前模式加载媒体"""
self.stop_capture()
mode = self.mode_combo.currentText()
if "图片" in mode:
self.load_image()
elif "视频" in mode:
self.load_video()
elif "摄像头" in mode:
self.start_camera()
def load_image(self):
options = QFileDialog.Options()
file_path, _ = QFileDialog.getOpenFileName(
self, "选择图片", "", "图像文件 (*.jpg *.jpeg *.png *.bmp);;All Files (*)", options=options
)
if not file_path:
return
image = cv2.imread(file_path)
if image is None:
self.result_label.setText("❌ 错误:无法读取图片")
return
result_img, text = self.process_frame(image)
self.show_cv_image(result_img)
self.result_label.setText(f"✅ 识别结果:{text}" if text else "❌ 未检测到有效车牌")
def load_video(self):
options = QFileDialog.Options()
file_path, _ = QFileDialog.getOpenFileName(
self, "选择视频", "", "视频文件 (*.mp4 *.avi *.mov);;All Files (*)", options=options
)
if not file_path:
return
self.cap = cv2.VideoCapture(file_path)
if not self.cap.isOpened():
self.result_label.setText("❌ 错误:无法打开视频文件")
return
self.result_label.setText("▶ 正在播放视频...")
self.timer.start(33) # ~30fps
def start_camera(self):
self.cap = cv2.VideoCapture(0)
if not self.cap.isOpened():
self.result_label.setText("❌ 错误:无法打开摄像头")
return
self.result_label.setText("📹 摄像头已启动...")
self.timer.start(30)
def update_frame(self):
"""定时更新帧(用于视频和摄像头)"""
if self.cap and self.cap.isOpened():
ret, frame = self.cap.read()
if ret:
result_img, text = self.process_frame(frame)
self.show_cv_image(result_img)
self.result_label.setText(f"✅ 实时识别结果:{text}" if text else "🔍 检测中...")
else:
self.timer.stop()
self.result_label.setText("🔚 视频播放结束")
def process_frame(self, image):
"""
处理单帧图像:重点增强蓝底白字 + OCR 识别
"""
# 🔍 预处理:增强蓝底白字对比度
processed = self.enhance_blue_white_plate(image)
# 使用 EasyOCR 进行端到端识别
results = self.reader.readtext(
processed,
detail=1,
paragraph=False,
low_text=0.1,
contrast_ths=0.1,
adjust_contrast=0.5,
allowlist="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼·"
)
if not results:
return image, ""
output_img = image.copy()
candidates = []
for (bbox, text, prob) in results:
points = np.array(bbox, dtype=np.int32)
cv2.polylines(output_img, [points], isClosed=True, color=(0, 255, 0), thickness=3)
cv2.putText(output_img, text[:10], tuple(points[0]), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 1)
cleaned = self.clean_text(text)
if self.is_possible_license_plate(cleaned):
candidates.append((cleaned, prob))
# 返回最可能的结果
if candidates:
best_guess = max(candidates, key=lambda x: x[1])
final_text = best_guess[0]
else:
all_texts = [self.clean_text(r[1]) for r in results]
final_text = max(all_texts, key=len) if all_texts else ""
return output_img, final_text
@staticmethod
def enhance_blue_white_plate(image):
"""
专门针对【蓝底白字】车牌增强
方法:
- 提取蓝色通道
- 白色字符在蓝底上更清晰
- 使用自适应阈值突出白色文字
"""
# 分离颜色通道
b, g, r = cv2.split(image)
# 蓝色通道通常对蓝牌响应最强
blue_channel = b
# 高斯模糊降噪
blurred = cv2.GaussianBlur(blue_channel, (5, 5), 0)
# 自适应二值化,突出白色字符
enhanced = cv2.adaptiveThreshold(
blurred, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY_INV,
19, 8
)
# 形态学闭操作连接断裂字符
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
enhanced = cv2.morphologyEx(enhanced, cv2.MORPH_CLOSE, kernel)
return enhanced
@staticmethod
def clean_text(text):
return re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9·]', '', text)
@staticmethod
def is_possible_license_plate(s):
pattern = r'^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]' \
r'[A-Z][A-Z0-9·]?[A-Z0-9]{4,6}$'
return re.match(pattern, s) is not None
def show_cv_image(self, cv_img):
rgb_img = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
h, w, ch = rgb_img.shape
bytes_per_line = ch * w
qt_img = QImage(rgb_img.data, w, h, bytes_per_line, QImage.Format_RGB888)
pixmap = QPixmap.fromImage(qt_img)
scaled_pixmap = pixmap.scaled(
self.video_label.size(),
Qt.KeepAspectRatio,
Qt.SmoothTransformation
)
self.video_label.setPixmap(scaled_pixmap)
def stop_capture(self):
"""释放资源"""
self.timer.stop()
if self.cap:
self.cap.release()
self.cap = None
def closeEvent(self, event):
self.stop_capture()
event.accept()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = LicensePlateRecognition()
window.show()
sys.exit(app.exec_())
```
---
## ✅ 功能说明
| 功能 | 状态 | 说明 |
|------|------|------|
| ✅ 蓝底白字增强 | ✔️ | 使用 `蓝色通道 + 自适应阈值` 提高识别率 |
| ✅ 图片识别 | ✔️ | 支持 JPG/PNG/BMP |
| ✅ 视频识别 | ✔️ | 支持 MP4/AVI/MOV |
| ✅ 摄像头识别 | ✔️ | 调用 `cv2.VideoCapture(0)` |
| ✅ 实时结果显示 | ✔️ | 在界面上绘制绿框和文本 |
| ✅ 中文省份+字母+数字 | ✔️ | 支持新能源“粤B·D12345”格式 |
---
## 🧰 安装依赖(必须执行)
```bash
pip install opencv-python easyocr PyQt5 numpy pillow regex
```
> ⚠️ 第一次运行会自动下载中文字体模型(约 20~30MB),请保持网络畅通。
---
## 💡 使用建议
| 场景 | 推荐设置 |
|------|----------|
| 白天室外拍摄 | 直接使用,默认参数即可 |
| 光线较暗环境 | 启用摄像头后手动补光 |
| 蓝牌识别不准 | 查看 `enhance_blue_white_plate()` 是否生效 |
| 想打包成 exe | 使用 PyInstaller 打包 |
---
###