收藏文章 楼主

二十行代码检测蓝色的交通标志牌

版块:IT/互联网   类型:普通   作者:小羊羔links   查看:649   回复:0   获赞:0   时间:2022-01-24 22:37:00


当行走在马路上时,不论是在人行道上,还是在车道隔离带上,都会看到大小不一,颜色各异的交通标志牌。其中,在高速上,大多数的交通标志牌是绿色的;在城市内,大多数的交通标志牌是蓝色的。本帖要实现的内容就和这些蓝色的交通标志牌相关,即在一幅如图1所示的街景图像中,检测蓝色矩形的交通标志牌,并用红色的线框把每一个蓝色矩形的交通标志牌标记出来,标记后的结果图像如图2所示。

图1  目标图像

图2  标记检测到的交通标志牌


本贴的实现步骤有4个 提取交通标志牌的蓝色区域,获取交通标志牌的轮廓,闭合交通标志牌的所在区域和标记交通标志牌的所在区域。下面将依次对这4个实现步骤进行讲解。


提取交通标志牌的蓝色区域

为了实现这个步骤,需要借助一个关键方法,即inRange()方法。通过inRange()方法把交通标志牌的蓝色区域变成白色,把除交通标志牌的蓝色区域外的区域变成黑色。本实例调用inRange()方法的代码如下所示

dst = cv2.inRange(hsv, lower_blue, upper_blue)

参数说明

?  hsv 一幅色彩空间是HSV的图像;

? lower_blue 在图像(hsv 中,对于像素值低于lower_blue的像素,将这些像素的值变为0;

?  upper_blue 在图像(hsv 中,对于像素值高于upper_blue的像素,将这些像素的值变为0。

说明 在图像(hsv 中,对于像素值介于lower_blue和upper_blue之间的像素,将这些像素的值变为255。


返回值说明

?  dst 掩模,即一幅只有黑色和白色的二值图像。


那么,应该如何理解lower_blue和upper_blue这两个标签,又该如下确定lower_blue和upper_blue这两个标签的值呢?


本步骤的目的是从目标图像中提取交通标志牌的蓝色区域,对于色彩空间是HSV的目标图像,lower_blue表示的是通过色调(H 、饱和度(S 和亮度(V 确定的蓝色范围内像素的最小值;upper_blue表示的是通过色调(H 、饱和度(S 和亮度(V 确定的蓝色范围内像素的最大值。


对于色彩空间是HSV的目标图像,其中的每一个像素都是一个含有3个元素的一维数组,这3个元素分别对应色调(H 、饱和度(S 和亮度(V 的值,因此调用Numpy中的array()方法即可确定lower_blue和upper_blue这两个标签的值。代码如下所示

01  lower_blue = np.array([100, 43, 46])

02  upper_blue = np.array([124, 255, 255])


但是,在目标图像中,除交通标志牌的背景色是蓝色的外,天空也是蓝色的。因此,天空的蓝色会对“从目标图像中提取交通标志牌的蓝色区域”的结果产生不小的影响。为此,需要修改如下代码

lower_blue = np.array([100, 43, 46])


“[100, 43, 46]”是通过色调(H 、饱和度(S 和亮度(V 确定的蓝色范围内像素的最小值。为了避免天空的蓝色对“从目标图像中提取交通标志牌的蓝色区域”的结果影响,需要对“[100, 43, 46]”进行调整,需要把饱和度(S 和亮度(V 的值都调整到50。调整后的代码如下所示

lower_blue = np.array([100, 50, 50])


经过inRange()方法处理后,就能够从目标图像中提取交通标志牌的蓝色区域,只不过处理后的图像是一幅如图3所示的二值图像。

图3  经inRange()方法处理后的图像


获取交通标志牌的轮廓

观察如图3所示的图像,会发现这幅图像含有噪声(3块交通标志牌的所在区域外含有分布不均的、白色的像素 。为了去除这幅图像中的噪声,需要使用一种用于对图像进行平滑处理的工具,即滤波器。本贴使用的滤波器是均值滤波器。


均值滤波器采用的是一个大小为9×9的滤波核,这样就能够编写使用均值滤波器对如图3所示的图像进行平滑处理的代码。代码如下所示

dst_blur = cv2.blur(dst, (9, 9))


使用均值滤波器对如图3所示的图像进行平滑处理后,处理后的图像虽然去除了噪声,但是会变得模糊不清。为了获取交通标志牌的轮廓,需要对进行平滑处理后的图像进行二值化阈值处理。


OpenCV提供了用于对图像进行阈值处理的threshold()方法。在对进行平滑处理后的图像进行二值化阈值处理时,采用的阈值是127、阈值的最大值是255。代码如下所示

ret, dst_threshold = cv2.threshold(dst_blur, 127, 255, cv2.THRESH_BINARY)

对进行平滑处理后的图像进行二值化阈值处理后的结果图像如图4所示。

图4  对进行平滑处理后的图像进行二值化阈值处理后的结果图像


闭合交通标志牌的所在区域

从图4不难看出,经二值化阈值处理后,进行平滑处理后的图像轮廓分明、对比明显。只不过,在图4中的交通标志牌的所在区域内,存在着 大 小的“小黑洞”。那么有没有什么办法能够消除这些 大 小的“小黑洞”,尽量闭合交通标志牌的所在区域?


为了解决这个问题,需要借助形态学操作中的闭运算。闭运算是一种基于腐蚀和膨胀的组合操作而形成的形态学操作,可以把闭运算的计算过程简单地理解为“先膨胀后腐蚀”。通过闭运算,能够扩张图4中的交通标志牌的所在区域内的白色部分,尽可能地消除其中的“小黑洞”,进而实现“尽量闭合交通标志牌的所在区域”的效果。


OpenCV提供了用于执行形态学操作的morphologyEx()方法,在这个方法的语法格式中有7个参数。在实际开发工作中,为了方便、快捷,只需要对其中的3个参数进行赋值即可。简化后的morphologyEx()方法的语法格式如下所示

dst = cv2.morphologyEx(src, op, kernel)

其中,参数src的值是dst_threshold,参数op的值是表示“闭运算”的cv2.MORPH_CLOSE。


那么,如何获取参数kernel的值呢?为此,OpenCV提供了用于生成不同形状的核的getStructuringElement()方法,其语法格式如下所示

kernel = cv2.getStructuringElement(shape, ksize)


参数说明

?  shape 核的形状(本实例采用的核的形状是“矩形” ;

?  ksize 核的尺寸,本实例采用的核的尺寸是“(21, 7)”。

结合getStructuringElement()方法和简化后的morphologyEx()方法,就能够消除在图4中的交通标志牌的所在区域内 大 小的“小黑洞”,尽可能地闭合交通标志牌的所在区域。代码如下所示


01  kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7))

02  closed = cv2.morphologyEx(dst_threshold, cv2.MORPH_CLOSE, kernel)

尽可能地闭合交通标志牌的所在区域后的结果图像如图5所示。

图5  尽可能地闭合交通标志牌的所在区域后的结果图像


标记交通标志牌的所在区域

要想标记交通标志牌的所在区域,就要先查找交通标志牌的所在区域的轮廓。OpenCV提供了用于查找一幅二值图像中的所有轮廓的findContours()方法。使用findContours()方法查找图5中的所有轮廓的代码如下所示

contours, hierarchy = cv2.findContours(closed, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

观察图5会发现交通标志牌的所在区域近似于矩形,因此要把在图5中找到的轮廓转换为矩形。也就是说,要根据找到的轮廓,生成这个轮廓的最小外接矩形。为此,OpenCV提供minAreaRect()方法,其语法格式如下所示

box = cv2.minAreaRect(points)

参数说明

?  points 点集;(本实例的“点集”就是在图5中找到的轮廓

返回值说明

?  box 是一个Box2D结构,其中包含最小外接矩形的中心坐标、宽度、高度和相对于水平方向的旋转角度等信息。


虽然通过minAreaRect()方法生成了一个轮廓的最小外接矩形,但是得到的不是这个最小外接矩形的4个顶点的坐标,而是一个Box2D结构。那么,如何根据这个Box2D结构获取最小外接矩形的4个顶点的坐标呢?为此,OpenCV提供了boxPoints()方法,其语法格式如下所示

retval = cv2.boxPoints(box)

参数说明

?  box 一个Box2D结构。

返回值说明

?  retval 根据这个Box2D结构获取最小外接矩形的4个顶点的坐标。

获取一个轮廓的最小外接矩形的4个顶点的坐标后,会发现这4个顶点的坐标是浮点型,而非整型。这里的“整型”区别于Python中的“整型”,指的是Numpy中的整型。因此,要调用Numpy中的int0()方法对这4个顶点进行取整。


既得到了一个轮廓的最小外接矩形,又得到了对这个最小外接矩形的4个顶点进行经取整后的坐标,那么就能够使用OpenCV中的drawContours()方法把这个最小外接矩形绘制出来。


根据以上内容,在通过findContours()方法找到图5中所有的交通标志牌的所在区域的轮廓后,先使用for循环遍历这些轮廓,再通过minAreaRect()方法得到每一个轮廓的最小外接矩形,接着通过boxPoints()方法得到这个最小外接矩形的4个顶点的坐标,而后通过int0()方法对这4个顶点进行取整,最后通过drawContours()方法把这个最小外接矩形在如图1所示的目标图像中用红色的边框绘制出来。实现上述步骤的代码如下所示

03  contours, hierarchy = cv2.findContours(closed, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

04  for con in contours:

05     box = cv2.minAreaRect(con) 

06      points_int = np.int0(cv2.boxPoints(box)) 

07      cv2.drawContours(img, [points_int], -1, (0, 0, 255), 2)


为了更深入地理解每一行代码的含义及其作用,下面将给出用于实现本实例的完整代码,代码如下所示

01  import cv2

02  import numpy as np

03  

04  img = cv2.imread("traffic.png")

05  hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)

06  

07  lower_blue = np.array([100, 50, 50])

08  upper_blue = np.array([124, 255, 255])

09  dst = cv2.inRange(hsv, lower_blue, upper_blue)

10  cv2.imshow("dst", dst)

11  

12  dst_blur = cv2.blur(dst, (9, 9))

13  ret, dst_threshold = cv2.threshold(dst_blur, 127, 255, cv2.THRESH_BINARY)

14  cv2.imshow("dst_threshold", dst_threshold)

15  

16  kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7))

17  closed = cv2.morphologyEx(dst_threshold, cv2.MORPH_CLOSE, kernel)

18  cv2.imshow("closed", closed)

19  

20  contours, hierarchy = cv2.findContours(closed, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

21  for con in contours:

22      box = cv2.minAreaRect(con)

23      points_int = np.int0(cv2.boxPoints(box))

24      cv2.drawContours(img, [points_int], -1, (0, 0, 255), 2)

25  cv2.imshow("result", img)

26  cv2.waitKey(0)

27  cv2.destroyAllWindows()


小羊羔锚文本外链网站长https://seo-links.cn 
回复列表
默认   热门   正序   倒序

回复:二十行代码检测蓝色的交通标志牌

Powered by 小羊羔外链网 8.3.11

©2015 - 2024 小羊羔外链网

免费发软文外链 鄂ICP备16014738号-6

您的IP:18.118.9.146,2024-04-20 05:43:03,Processed in 0.05563 second(s).

支持原创软件,抵制盗版,共创美好明天!
头像

用户名:

粉丝数:

签名:

资料 关注 好友 消息