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

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

1.前言

在上一篇Python+Opencv简易车牌识别(一):基于HSV颜色空间的图像分割中,我们讲了如何仅基于颜色来进行简单粗暴的车牌分割。今天我们考虑对图像进行一些更复杂的预处理,来使我们的程序能够识别更复杂情境下的车牌。
原图依旧如下:
在这里插入图片描述
完整代码于文章末尾给出。

2.分析

2.1.读取图片

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

# -*- coding: utf-8 -*-
import cv2
import 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 = 16
h = w = r * 2 + 1
kernel = 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 = 0
max_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 = 0
    for m in mask:
        print(m)
        w1 += m / 255
    w2 = 0
    for n in w1:
        w2 += n
    if w2 > max_weight:
        max_index = i
        max_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 cv2
import 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 = 16
h = w = r * 2 + 1
kernel = 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 = 0
max_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 = 0
    for m in mask:
        w1 += m / 255
    w2 = 0
    for n in w1:
        w2 += n
    if w2 > max_weight:
        max_index = i
        max_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()
  • 6
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值