车牌字符分割时出现额外轮廓的技术求助
车牌字符分割:解决形态学操作产生的额外轮廓问题
针对车牌字符分割中形态学操作产生额外轮廓(左侧片段、右侧线条)的问题,结合车牌字符的几何特征与结构规则,可通过降噪+特征过滤+结构兜底的组合策略优化处理流程:
改进后的处理代码
import cv2 import numpy as np # 假设ex_plate是透视变换后的灰度车牌图像 mask = cv2.adaptiveThreshold( ex_plate, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 17, 5 ) # 前置高斯模糊:提前过滤局部噪点,减少阈值后的无效轮廓 mask = cv2.GaussianBlur(mask, (3, 3), 0) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, np.ones((3, 3))) # 微调腐蚀核:根据字符宽度调整,避免过度腐蚀有效字符 marker = cv2.erode(mask, cv2.getStructuringElement(cv2.MORPH_RECT, (3, 18))) obr = open_by_reconstruction(marker, mask) # 自定义边界清除:只清除边缘5像素内的轮廓,避免误删靠近边缘的首字符 def custom_clear_border(img, border_width=5): h, w = img.shape img[:border_width, :] = 0 img[-border_width:, :] = 0 img[:, :border_width] = 0 img[:, -border_width:] = 0 return img obr = custom_clear_border(obr) obr = cv2.morphologyEx(obr, cv2.MORPH_CLOSE, np.ones((3, 3))) obr = hole_filler(obr) # 核心步骤:轮廓检测+几何特征过滤 contours, _ = cv2.findContours(obr, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 根据国内车牌字符特征定义过滤阈值(可根据实际场景调整) plate_h = obr.shape[0] min_char_h = int(plate_h * 0.6) max_char_h = plate_h min_char_w = int(plate_h * 0.3) max_char_w = int(plate_h * 0.6) min_char_area = min_char_h * min_char_w * 0.5 filtered_contours = [] for cnt in contours: x, y, w, h = cv2.boundingRect(cnt) aspect_ratio = w / h area = cv2.contourArea(cnt) # 过滤不符合字符宽高比、面积、高度的轮廓 if (min_char_h <= h <= max_char_h and min_char_w <= w <= max_char_w and 0.3 <= aspect_ratio <= 0.6 and area >= min_char_area): filtered_contours.append(cnt) # 按x坐标排序,保证字符顺序从左到右 filtered_contours = sorted(filtered_contours, key=lambda c: cv2.boundingRect(c)[0]) # 提取字符ROI,适当扩展边界避免裁剪 char_rois = [] for cnt in filtered_contours: x, y, w, h = cv2.boundingRect(cnt) roi = obr[max(0, y-2):min(plate_h, y+h+2), max(0, x-2):min(obr.shape[1], x+w+2)] char_rois.append(roi) # 兜底策略:若轮廓数量不符合(如非7个),按固定区域分割 if len(char_rois) != 7: plate_w = obr.shape[1] seg_w = plate_w // 7 char_rois = [] for i in range(7): start_x = i * seg_w end_x = plate_w if i == 6 else (i+1)*seg_w segment = obr[:, start_x:end_x] seg_contours, _ = cv2.findContours(segment, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if seg_contours: # 取区域内面积最大的轮廓作为字符 max_cnt = max(seg_contours, key=cv2.contourArea) x, y, w, h = cv2.boundingRect(max_cnt) roi = segment[y:y+h, x:x+w] char_rois.append(roi)
关键优化点说明
- 前置高斯模糊:在阈值处理前加入模糊,直接减少局部噪点产生的无效轮廓,降低后续形态学操作的压力。
- 自定义边界清除:替换默认
clear_border,只清除边缘少量像素内的轮廓,避免误删车牌首字符。 - 几何特征过滤:利用车牌字符固定的宽高比(0.3-0.6)、高度占比(60%-100%车牌高度)、面积阈值,精准过滤左侧小片段和右侧长线条类干扰。
- 结构兜底策略:当轮廓检测数量不符合车牌字符数时,强制按水平方向分割7个区域,每个区域取最大轮廓,保证分割结果的完整性。
额外建议
- 若仍有干扰,可尝试Otsu全局阈值+局部自适应阈值的组合,提升复杂光照下的二值化效果。
- 加入直方图均衡化(
cv2.equalizeHist)增强字符与背景的对比度,降低分割难度。
内容的提问来源于stack exchange,提问作者m.marian
相关产品推荐
相关产品推荐

