700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Python+Opencv简易车牌识别(二):形态学运算 HSV颜色空间筛选与图像分割

Python+Opencv简易车牌识别(二):形态学运算 HSV颜色空间筛选与图像分割

时间:2022-07-01 18:31:05

相关推荐

Python+Opencv简易车牌识别(二):形态学运算 HSV颜色空间筛选与图像分割

注:这是依然一个简单的车牌识别demo

1.前言

在上一篇Python+Opencv简易车牌识别(一):基于HSV颜色空间的图像分割中,我们讲了如何仅基于颜色来进行简单粗暴的车牌分割。今天我们考虑对图像进行一些更复杂的预处理,来使我们的程序能够识别更复杂情境下的车牌。

原图依旧如下:

完整代码于文章末尾给出。

2.分析

2.1.读取图片

程序开始,读取图片。在这里我们将图片进行等比例放缩,宽度固定为400像素,以防止图片分辨率过高影响处理的性能。然后将图片转化为灰度图以供进一步的处理。

# -*- coding: utf-8 -*-import cv2import numpy as np#读取图片img = cv2.imread(r'D:\car1.png')m = 400 * img.shape[0] / img.shape[1]#调整图像尺寸img = cv2.resize(img, (400, int(m)), interpolation = cv2.INTER_AREA)#转化为灰度图gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

这一阶段得到的灰度图gray_img长这样:

2.2.图像分割

2.2.1.顶帽

在这一阶段,我们采用开运算闭运算这两种形态学运算的方法来对图像进行分割。

开运算:

是一个基于几何运算的滤波器本质是先腐蚀运算,再膨胀运算能够除去孤立的小点,毛刺,并保持总体的位置和形状不发生改变

闭运算:

是一个基于几何运算的滤波器本质是先膨胀运算,再腐蚀运算能够填平小孔,弥合小裂缝,并保持总体的位置和形状不发生改变

现在,我们先计算一个顶帽。所谓顶帽:

值为原图像 - 开运算开运算能放大裂缝或者局部亮度较低区域因此从原图中减去开运算后的图,能够突出比轮廓周围更明亮的区域可以用来分离比邻近点亮一些的亮斑。在一幅图像具有大幅的背景,而微小物品比较有规律的情况下,可以使用顶帽进行背景提取

#结构元素(kernel)为半径为16的圆r = 16h = w = r * 2 + 1kernel = np.zeros((h, w), np.uint8)cv2.circle(kernel, (r, r), r, 1, -1)#开运算open_img = cv2.morphologyEx(gray_img, cv2.MORPH_OPEN, kernel)#顶帽hat_img = cv2.absdiff(gray_img, open_img)

这一阶段得到的顶帽hat_img长这样:

可以看到背景变暗,而车牌、车标等较亮区域得到了增强。

2.2.2.边缘检测

我们上一步增强了图像中几个较亮的区域,其中便包括了我们感兴趣的车牌。现在我们用Canny算子进行边缘检测。

注意在边缘检测之前,要先对图像进行二值化处理。

#二值化函数def binaryzation(img):maxi = float(img.max())mini = float(img.min())x = maxi - ((maxi - mini) / 2)ret, thresh = cv2.threshold(img, x, 255, cv2.THRESH_BINARY)return thresh#图像二值化binary_img = binaryzation(hat_img)#canny边缘检测canny = cv2.Canny(binary_img, binary_img.shape[0], binary_img.shape[1])

这一阶段得到的边缘检测结果canny长这样:

2.2.3.填充边缘

边缘检测后能够得到线条形式的图像边缘。现在我们利用闭运算来填平小孔,对边缘内部进行填充:

#闭运算核kernel = np.ones((5, 19), np.uint8)#闭运算close_img = cv2.morphologyEx(canny, cv2.MORPH_CLOSE, kernel)

这一阶段得到的闭运算图像close_img长这样:

2.2.4.噪声消除

可以看到经过上一步的边缘填充后,我们车牌的主体区域已经被标记出来了,但除此之外图像中还存在着许多小的噪点,因此我们进行连续两次开运算尝试把它们消去:

#开运算open_img = cv2.morphologyEx(close_img, cv2.MORPH_OPEN, kernel)#更换结构元素kernel = np.ones((11, 5), np.uint8)#开运算open_img = cv2.morphologyEx(open_img, cv2.MORPH_OPEN, kernel)

这一阶段得到的开运算图像open_img长这样:

可见噪声已被大大消除,图像被分割成了8个区域。接下来我们要做的是从这8个区块中找到我们想要的车牌。

2.3.车牌定位

2.3.1.位置提取

在上一节中我们从可视化结果的角度,明确了图片被分割成了8个区域,那么如何获得这8个区域的位置特征(如坐标)呢?

先给出代码:

#找到能够包围给定区块的最小矩形的左下角坐标(min(x),min(y))与右上角坐标(max(x),max(y))def find_rectangle(contour):x = []y = []for p in contour:y.append(p[0][0])x.append(p[0][1]) return [min(y), min(x), max(y), max(x)]#提取轮廓contours, hierarchy = cv2.findContours(open_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)#存储每个块的矩形坐标block = []for c in contours:r = find_rectangle(c)block.append(r)

首先是这个findContours函数,其功能为提取区域的边缘。而在cv2.RETR_EXTERNAL模式下,会返回边缘"顶点"坐标构成的数组,例如对于矩形会有四个边缘顶点,三角形会有三个边缘顶点。

然后,对于每个区域,我们尝试找到一个最小的矩形来将其覆盖,这就是find_rectangle方法的作用。

2.3.2.基于HSV颜色空间的筛选

上一篇我们采用的也是基于颜色空间筛选的思想来分割车牌。在这里,我们先将分割出来的图像区域b转化成hsv颜色空间,然后利用"掩膜"来筛选出区域中位于上下界范围内的像素点。最后,对比8个区域颜色范围的满足情况,最符合蓝色颜色区间的区域便是车牌区域。

max_weight = 0max_index = -1#遍历分割出来的各个区域for i in range(len(block)):b = img[block[i][1] : block[i][3], block[i][0] : block[i][2]]#转化为hsv颜色空间hsv = cv2.cvtColor(b, cv2.COLOR_BGR2HSV)#蓝色下界lower = np.array([100, 50, 50])#蓝色上界upper = np.array([140, 255, 255])#利用掩膜进行筛选mask = cv2.inRange(hsv, lower, upper)#计算当前区域的满足情况w1 = 0for m in mask:print(m)w1 += m / 255w2 = 0for n in w1:w2 += nif w2 > max_weight:max_index = imax_weight = w2

现在我们看看选出来的"最可能为车牌"的区域长啥样:

cv2.imshow('ovo', img[block[max_index][1] : block[max_index][3], block[max_index][0] : block[max_index][2]])cv2.waitKey(0)

2.3.3.标注

最后给选出的区域上框就好了:

#最可能为车牌的区域对应的矩形坐标rect = block[max_index]#画框cv2.rectangle(img, (rect[0], rect[1]), (rect[2], rect[3]),(0, 255, 0), 2)cv2.imshow('ovo', img)cv2.waitKey(0)cv2.destroyAllWindows()

3.完整代码

# -*- coding: utf-8 -*-import cv2import numpy as np#二值化def binaryzation(img):maxi = float(img.max())mini = float(img.min())x = maxi - ((maxi - mini) / 2)ret, thresh = cv2.threshold(img, x, 255, cv2.THRESH_BINARY)return thresh#找到能够包围给定区块的最小矩形的左下角坐标(min(x),min(y))与右上角坐标(max(x),max(y))def find_rectangle(contour):x = []y = []for p in contour:y.append(p[0][0])x.append(p[0][1]) return [min(y), min(x), max(y), max(x)]#读取图片img = cv2.imread(r'D:\car1.png')m = 400 * img.shape[0] / img.shape[1]#调整图像尺寸img = cv2.resize(img, (400, int(m)), interpolation = cv2.INTER_AREA)#转化为灰度图gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#结构元素(kernel)为半径为16的圆r = 16h = w = r * 2 + 1kernel = np.zeros((h, w), np.uint8)cv2.circle(kernel, (r, r), r, 1, -1)#开运算open_img = cv2.morphologyEx(gray_img, cv2.MORPH_OPEN, kernel)#顶帽hat_img = cv2.absdiff(gray_img, open_img)#图像二值化binary_img = binaryzation(hat_img)#canny边缘检测canny = cv2.Canny(binary_img, binary_img.shape[0], binary_img.shape[1])#闭运算核kernel = np.ones((5, 19), np.uint8)#闭运算close_img = cv2.morphologyEx(canny, cv2.MORPH_CLOSE, kernel)#开运算open_img = cv2.morphologyEx(close_img, cv2.MORPH_OPEN, kernel)#更换结构元素kernel = np.ones((11, 5), np.uint8)#开运算open_img = cv2.morphologyEx(open_img, cv2.MORPH_OPEN, kernel)#提取轮廓contours, hierarchy = cv2.findContours(open_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)#存储每个块的矩形坐标block = []for c in contours:r = find_rectangle(c)block.append(r)max_weight = 0max_index = -1#遍历分割出来的各个区域for i in range(len(block)):b = img[block[i][1] : block[i][3], block[i][0] : block[i][2]]#转化为hsv颜色空间hsv = cv2.cvtColor(b, cv2.COLOR_BGR2HSV)#蓝色下界lower = np.array([100, 50, 50])#蓝色上界upper = np.array([140, 255, 255])#利用掩膜进行筛选mask = cv2.inRange(hsv, lower, upper)#计算当前区域的满足情况w1 = 0for m in mask:w1 += m / 255w2 = 0for n in w1:w2 += nif w2 > max_weight:max_index = imax_weight = w2#最可能为车牌的区域对应的矩形坐标rect = block[max_index]#画框cv2.rectangle(img, (rect[0], rect[1]), (rect[2], rect[3]),(0, 255, 0), 2)cv2.imshow('ovo', img)cv2.waitKey(0)cv2.destroyAllWindows()

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。