在交通实景图中检测出交通标志,并将区域裁剪,为后续的识别做准备。
import cv2
import numpy as np
#加载原图
img=cv2.imread('walks.jpg')
print('img:',type(img),img.shape,img.dtype)
cv2.imshow('img',img)
hsv=cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
cv2.imshow('hsv',hsv)
确定提取的蓝色范围(HSV颜色),然后通过inRange函数(参数图片为HSV格式)提取蓝色区域
#提取蓝色区域
blue_lower=np.array([100,50,50])
blue_upper=np.array([124,255,255])
mask=cv2.inRange(hsv,blue_lower,blue_upper)
print('mask',type(mask),mask.shape)
cv2.imshow('mask',mask)
如图所示,颜色区域在mask中白色表示,,其他颜色过滤,为黑色,人行道的标志 基本出来,但是实际场景图有许多颜色干扰,所以需要进一步优化。
#模糊
blurred=cv2.blur(mask,(9,9))
cv2.imshow('blurred',blurred)
将模糊后图像转换为二值图,只有0和1表示颜色。
#二值化
ret,binary=cv2.threshold(blurred,127,255,cv2.THRESH_BINARY)
cv2.imshow('blurred binary',binary)
目的在于封闭区域,无空隙。
#使区域闭合无空隙
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7))
closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
cv2.imshow('closed',closed)
此时人行道区域已经明显呈现,且无杂色干扰。
如果场景与比较复杂,仍然存在颜色干扰,可以采用膨胀和腐蚀操作进行去除干扰。
#腐蚀和膨胀
'''
腐蚀操作将会腐蚀图像中白色像素,以此来消除小斑点,
而膨胀操作将使剩余的白色像素扩张并重新增长回去。
'''
erode=cv2.erode(closed,None,iterations=4)
cv2.imshow('erode',erode)
dilate=cv2.dilate(erode,None,iterations=4)
cv2.imshow('dilate',dilate)
利用腐蚀膨胀去除干扰色的完整实例可参考基于OpenCV-python3实现证件照换背景
思路:此时的图像已经处理的很简单,所以进行查找轮廓,将轮廓转换为矩形,然后根据矩形的坐标,在原图进行裁剪,即可得到目标区域
# 查找轮廓
image,contours, hierarchy=cv2.findContours(dilate.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
print('轮廓个数:',len(contours))
i=0
res=img.copy()
for con in contours:
#轮廓转换为矩形
rect=cv2.minAreaRect(con)
#矩形转换为box
box=np.int0(cv2.boxPoints(rect))
#在原图画出目标区域
cv2.drawContours(res,[box],-1,(0,0,255),2)
print([box])
#计算矩形的行列
h1=max([box][0][0][1],[box][0][1][1],[box][0][2][1],[box][0][3][1])
h2=min([box][0][0][1],[box][0][1][1],[box][0][2][1],[box][0][3][1])
l1=max([box][0][0][0],[box][0][1][0],[box][0][2][0],[box][0][3][0])
l2=min([box][0][0][0],[box][0][1][0],[box][0][2][0],[box][0][3][0])
print('h1',h1)
print('h2',h2)
print('l1',l1)
print('l2',l2)
#加上防错处理,确保裁剪区域无异常
if h1-h2>0 and l1-l2>0:
#裁剪矩形区域
temp=img[h2:h1,l2:l1]
i=i+1
#显示裁剪后的标志
cv2.imshow('sign'+str(i),temp)
#显示画了标志的原图
cv2.imshow('res',res)
cv2.waitKey(0)
cv2.destroyAllWindows()
完整代码
import cv2
import numpy as np
#加载原图
img=cv2.imread('jd.jpg')
print('img:',type(img),img.shape,img.dtype)
cv2.imshow('img',img)
hsv=cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
cv2.imshow('hsv',hsv)
#提取蓝色区域
blue_lower=np.array([100,50,50])
blue_upper=np.array([124,255,255])
mask=cv2.inRange(hsv,blue_lower,blue_upper)
print('mask',type(mask),mask.shape)
cv2.imshow('mask',mask)
#模糊
blurred=cv2.blur(mask,(9,9))
cv2.imshow('blurred',blurred)
#二值化
ret,binary=cv2.threshold(blurred,127,255,cv2.THRESH_BINARY)
cv2.imshow('blurred binary',binary)
#使区域闭合无空隙
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (21, 7))
closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
cv2.imshow('closed',closed)
#腐蚀和膨胀
'''
腐蚀操作将会腐蚀图像中白色像素,以此来消除小斑点,
而膨胀操作将使剩余的白色像素扩张并重新增长回去。
'''
erode=cv2.erode(closed,None,iterations=4)
cv2.imshow('erode',erode)
dilate=cv2.dilate(erode,None,iterations=4)
cv2.imshow('dilate',dilate)
# 查找轮廓
image,contours, hierarchy=cv2.findContours(dilate.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
print('轮廓个数:',len(contours))
i=0
res=img.copy()
for con in contours:
#轮廓转换为矩形
rect=cv2.minAreaRect(con)
#矩形转换为box
box=np.int0(cv2.boxPoints(rect))
#在原图画出目标区域
cv2.drawContours(res,[box],-1,(0,0,255),2)
print([box])
#计算矩形的行列
h1=max([box][0][0][1],[box][0][1][1],[box][0][2][1],[box][0][3][1])
h2=min([box][0][0][1],[box][0][1][1],[box][0][2][1],[box][0][3][1])
l1=max([box][0][0][0],[box][0][1][0],[box][0][2][0],[box][0][3][0])
l2=min([box][0][0][0],[box][0][1][0],[box][0][2][0],[box][0][3][0])
print('h1',h1)
print('h2',h2)
print('l1',l1)
print('l2',l2)
#加上防错处理,确保裁剪区域无异常
if h1-h2>0 and l1-l2>0:
#裁剪矩形区域
temp=img[h2:h1,l2:l1]
i=i+1
#显示裁剪后的标志
cv2.imshow('sign'+str(i),temp)
#显示画了标志的原图
cv2.imshow('res',res)
cv2.waitKey(0)
cv2.destroyAllWindows()
在复杂场景下,经过优化处理仍然会存在干扰区域,可以在查找轮廓和裁剪区域时候进行选择性裁剪,比如首先对区域进行面积周长比等判断是否为 所想要裁剪的区域,个人认为在交通标志检测中用长宽比进行过滤比较简单高效,因为常见的标志基本形状为正三角形,圆,正方形。在轮廓进行矩形处理之后所得到的区域都会是正方形(即正三角形,圆的外接矩形都是正方形)。所以裁剪时候,长宽比接近1的保留,差距太大的直接丢弃。
仅个人想法,欢迎交流,以上。