车牌识别是计算机视频图像识别技术在车辆牌照识别中的一种应用,通常来讲如果结合opencv进行车牌识别主要分为四个大步骤,分别为:
现在我们目的是从一张图片中识别出车牌位置,并将车牌位置的矩形单独取出来形成一张图片。为了达到这样的目的我们需要完成以下几步:
刚才说到,尺寸调整的目的是避免因图片尺寸过大而引起计算机处理速度过慢,在这里需要重点注意的是在调整尺寸的时候需要注意保持好图片的宽高比,假设我们需要做的是将图片的宽度变为500px,并同时假设我们手中的图片是19201080px,那么我们最终处理好的图片应该是889500,即500/1080*1920
以下代码来自网络,在python opencv中保持宽高比地处理图片:
def resize_keep_aspectratio(image_src, dst_size): src_h, src_w = image_src.shape[:2] # print(src_h, src_w) dst_h, dst_w = dst_size # 判断应该按哪个边做等比缩放 h = dst_w * (float(src_h) / src_w) # 按照w做等比缩放 w = dst_h * (float(src_w) / src_h) # 按照h做等比缩放 h = int(h) w = int(w) if h <= dst_h: image_dst = cv2.resize(image_src, (dst_w, int(h))) else: image_dst = cv2.resize(image_src, (int(w), dst_h)) h_, w_ = image_dst.shape[:2] return image_dst我们使用img = self.resize_keep_aspectratio(image, [500, 500])即可进行调用,其中虽然传递为[500,500],实际上改方法会根据宽高比自动调整。
色域分析的关键函数是调用cv2.inRange方法,围绕该方法我们写出以下代码:
# hsv提取蓝色部分 def hsv_color_find(img): img_copy = img.copy() """ 提取图中的蓝色部分 hsv范围可以自行优化 """ hsv = cv2.cvtColor(img_copy, cv2.COLOR_BGR2HSV) low_hsv = np.array([100, 80, 80]) high_hsv = np.array([124, 255, 255]) # 设置HSV的阈值 mask = cv2.inRange(hsv, lowerb=low_hsv, upperb=high_hsv) cv2.imshow("hsv_color_find", mask) # 将掩膜与图像层逐像素相加 res = cv2.bitwise_and(img_copy, img_copy, mask=mask) cv2.imshow("hsv_color_find2", res) return res其中low_hsv以及high_hsv是根据经验设定的在hsv色域取出蓝色的值,最终效果如下:
原图:
进行色域分析后的图:
进行色域分析后进行叠加的图:
# RGB->灰度 gray_img = cv2.cvtColor(img_copy, cv2.COLOR_BGR2GRAY) gray_img_ = cv2.GaussianBlur(gray_img, (5, 5), 0, 0, cv2.BORDER_DEFAULT)其中(5, 5)也是经验值,可以自行调整
经过灰度处理和高斯过滤后结果如下:
kernel = np.ones((23, 23), np.uint8) # 使用侵蚀和膨胀作为基本操作来执行高级形态转换。任何操作都可以就地完成.在多通道图像的情况下,每个通道都是独立处理的. img_opening = cv2.morphologyEx(gray_img, cv2.MORPH_OPEN, kernel) # 计算两个数组的加权和 img_opening = cv2.addWeighted(gray_img, 1, img_opening, -1, 0)进行形态学处理的主要方法是cv2.morphologyEx,里面包括了两个重要的参数,对于这两个重要的参数可以通过搜索引擎得知其用途。
处理过后效果如下:
ret2, img_thresh2 = cv2.threshold(img_opening, 0, 255, cv2.THRESH_BINARY)
效果如下:
# # 使用开运算和闭运算让图像边缘成为一个整体 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10)) img_edge1 = cv2.morphologyEx(img_edge, cv2.MORPH_CLOSE, kernel) img_edge2 = cv2.morphologyEx(img_edge1, cv2.MORPH_OPEN, kernel) img_edge3 = cv2.morphologyEx(img_thresh2, cv2.MORPH_CLOSE, kernel) img_edge4 = cv2.morphologyEx(img_edge3, cv2.MORPH_CLOSE, kernel) contours, hierarchy = cv2.findContours(img_edge4, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)真正检测边缘的代码实际上就是cv2.findContours
但是在真正进行检测的时候还做了几次开运算和闭运算,这样可以使碎片化的二值化图像形成一个整体,以便后续进行整体识别。
识别过后针对边缘画出的边缘图像如下:
从上一步得知,我们进行边缘检测得到的边缘多达9个,我们需要从这些边缘中找出车牌号的那块,OpenCV自带的cv2.contourArea()函数可以实现计算点集(轮廓)所围区域的面积,cv2.minAreaRect()函数可以计算出点集的最小外包旋转矩形,cv2.boxPoints()函数可以根据旋转矩形的中心的坐标、尺寸和旋转角度,计算出旋转矩形的四个顶点。
我们可以通过测量知道中国的蓝牌和黑牌是440×140,黄牌前牌尺寸同,后牌为440×220;摩托车及轻便摩托车前牌是220×95,后牌是220×140。我们可以通如下代码筛选:
## 先筛选面积较大的地方 for contour in contours: if cv2.contourArea(contour) > 2000: temp_contours.append(contour) for temp_contour in temp_contours: rect_tupple = cv2.minAreaRect(temp_contour) rect_width, rect_height = rect_tupple[1] if rect_width < rect_height: rect_width, rect_height = rect_height, rect_width aspect_ratio = rect_width / rect_height if aspect_ratio > 1.5 and aspect_ratio < 4.65: #此处可根据具体的情况针对比例进行具体的调整 car_plate1.append(temp_contour) rect_vertices = cv2.boxPoints(rect_tupple) #最后进行矩阵和投影变换,将斜着的矩形摆正 rect = cv2.minAreaRect(car_plate) # 计算最小区域的坐标 box = cv2.boxPoints(rect) wh = np.int0(rect[1]) # 变换矩阵 if 0<rect[2]<=45: mat = cv2.getPerspectiveTransform(np.float32([[box[0][0], box[0][1]], [box[1][0], box[1][1]], [box[2][0], box[2][1]], [box[3][0], box[3][1]]]),np.float32([[0, wh[1]], [0, 0], [wh[0], 0], [wh[0], wh[1]]])) # 投影变换 lic = cv2.warpPerspective(img, mat, (wh[0], wh[1])) if 45 < rect[2] <= 90: mat = cv2.getPerspectiveTransform(np.float32( [[box[0][0], box[0][1]], [box[1][0], box[1][1]], [box[2][0], box[2][1]], [box[3][0], box[3][1]]]), np.float32([[0, 0], [wh[1], 0], [wh[1], wh[0]], [0, wh[0]]])) # 投影变换 lic = cv2.warpPerspective(img, mat, (wh[1], wh[0])) cv2.imshow("card_img", lic)得到的结果如下:
至此,我们完成了车牌的提取,得到这张图片后我们可以即可以进行后续的操作!