Python编写的OpenCV车牌识别系统,使用PyQt5编写界面,附工作流程
实测发现最大的瓶颈在字符识别部分,后来把Tesseract换成轻量化的CNN模型后,速度直接翻倍。这段代码里有个坑要注意:morphologyEx用的结构核尺寸(17,5)是经验值,实际用的时候得根据摄像头分辨率调整。最近在捣鼓车牌识别的小项目,发现用Python+OpenCV搞图像处理配上PyQt5做界面,效果居然还不错。有个坑是新能源车的第八位字符比较窄,容易和后面的圆点粘连,得在投影分析后加
python编写的opencv车牌识别,使用pyqt5写界面,附工作流程
最近在捣鼓车牌识别的小项目,发现用Python+OpenCV搞图像处理配上PyQt5做界面,效果居然还不错。今儿就带大家看看这玩意怎么从零开始搭建,顺便扒拉点实战代码出来。

先看整个流程:摄像头抓图→图像预处理→车牌定位→字符分割→文字识别→结果显示。咱们先从最刺激的车牌定位开始,这里用OpenCV的灰度化+边缘检测三板斧:
def locate_plate(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
# Sobel边缘检测
sobel = cv2.Sobel(blur, cv2.CV_16S, 1, 0)
sobel = cv2.convertScaleAbs(sobel)
# 自适应阈值处理
ret, binary = cv2.threshold(sobel, 0, 255, cv2.THRESH_OTSU)
# 形态学闭操作
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (17,5))
closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
return closed
这段代码里有个坑要注意:morphologyEx用的结构核尺寸(17,5)是经验值,实际用的时候得根据摄像头分辨率调整。有次我偷懒直接用了默认值,结果把马路牙子当车牌框了...
定位到候选区域后,得用轮廓分析筛出真正的车牌。这里用到了轮廓面积和长宽比过滤:
contours, _ = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
area = cv2.contourArea(cnt)
if area < 2000: continue
rect = cv2.minAreaRect(cnt)
w, h = rect[1]
ratio = max(w,h)/min(w,h) if min(w,h)>0 else 0
if 2.5 < ratio < 5.5: # 典型车牌长宽比
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(img, [box], 0, (0,255,0), 2)
这里有个骚操作——用minAreaRect获取旋转矩形,比直接用boundingRect更能适应倾斜车牌。实测在停车场斜着拍的车牌,用这法子框选准确率能提升40%左右。

python编写的opencv车牌识别,使用pyqt5写界面,附工作流程
字符分割这块儿最容易翻车,特别是碰到新能源车牌中间的小圆点。我的方案是垂直投影法+连通域分析:
def split_chars(plate_img):
# 二值化反转(白底黑字)
_, binary = cv2.threshold(plate_img, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# 垂直投影
v_projection = np.sum(binary, axis=0)
# 找波峰波谷
char_ranges = []
in_char = False
for i, val in enumerate(v_projection):
if val > 0 and not in_char:
start = i
in_char = True
elif val == 0 and in_char:
end = i
in_char = False
if end - start > 5: # 过滤噪点
char_ranges.append((start, end))
return [binary[:,s:e] for s,e in char_ranges]
注意这里把图像反相处理了,白底黑字更符合OCR的输入要求。有个坑是新能源车的第八位字符比较窄,容易和后面的圆点粘连,得在投影分析后加个宽度过滤。

最后是PyQt5的界面设计,这里用QCamera做实时视频流显示:
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.camera = QCamera()
self.viewfinder = QCameraViewfinder()
self.camera.setViewfinder(self.viewfinder)
# 自定义画面处理
self.camera.imageCaptured.connect(self.process_frame)
# 结果显示区域
self.result_label = QLabel('识别结果')
layout = QVBoxLayout()
layout.addWidget(self.viewfinder)
layout.addWidget(self.result_label)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
def process_frame(self, image):
# 转换OpenCV格式
ptr = image.bits()
ptr.setsize(image.byteCount())
arr = np.array(ptr).reshape(image.height(), image.width(), 4)
frame = cv2.cvtColor(arr, cv2.COLOR_RGBA2BGR)
# 调用识别管道
result = pipeline(frame)
self.result_label.setText(result)
这里有个性能优化点:直接操作图像内存比用QImage转格式快3倍不止。但要注意线程安全问题,图像处理部分最好放到单独的工作线程,否则界面会卡成PPT。
整套系统跑起来后,在i5-8265U上能达到每秒5帧的处理速度。实测发现最大的瓶颈在字符识别部分,后来把Tesseract换成轻量化的CNN模型后,速度直接翻倍。不过这事儿得另开一篇细说了,毕竟训练模型又是另一个深坑...
更多推荐
所有评论(0)