一、实现效果如下(几乎没有泛化性,但是我看别的文章好像也是有这种问题)
定位的效果
字符分割的效果
二、 车牌定位的实现
就是在这张车屁股的照片里定位到车牌并提取出来,涉及到的图像处理步骤如下
先转为灰度图、进行一个高斯滤波(减小噪声的影响),开运算(原理是先腐蚀后膨胀,效果是能够除去孤立的小点,毛刺和小桥,而总的位置和形状不便),闭运算(与开运算相反,有助于关闭前景物体内部的小孔,或物体上的小黑点),canny边缘检测,二值化处理.
重点:
cv2.findContours,查找二值图像中的轮廓 ,函数首先返回一个list,list中每个元素都是图像中的一个轮廓,用numpy中的ndarray表示。之后在依次计算每个轮廓的面积和面积的长宽比,对轮廓进行一个筛选之后得到这牌的位置。
#实现函数的定位
def getPostion(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_copy = img_gray.copy()
#高斯滤波 将噪声
gray_img = cv2.GaussianBlur(img_copy, (5, 5), 0, 0, cv2.BORDER_DEFAULT)
#开运算 将图像中不同部分分开
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) # 开运算后的图片与原始的灰度图片按一定权重进行一个融合
# 找到图像边缘
ret, img_thresh = cv2.threshold(img_opening, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 阈值分割
img_edge = cv2.Canny(img_thresh, 100, 200) #canny 边缘检测
# # 使用开运算和闭运算让图像边缘成为一个整体
kernel = np.ones((9, 9), np.uint8)
img_edge2 = cv2.morphologyEx(img_edge, cv2.MORPH_CLOSE, kernel)
#img_edge2 = cv2.morphologyEx(img_edge1, cv2.MORPH_OPEN, kernel) # 我测试时发现有这句代码,开运算就会把中文那部分去除
# # 查找图像边缘整体形成的矩形区域,可能有很多,车牌就在其中一个矩形区域中
contours, hierarchy = cv2.findContours(img_edge2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#从找到的轮廓中找出车牌的位置
temp_contours = []
areaAverage = []
for contour in contours:
area = cv2.contourArea(contour)
# print("面积 ", area)
areaAverage.append(area)
minArea = np.mean(areaAverage)
#print("平均面积", minArea) #我们假设车牌的面积一定大于平均面积
temp_contours = [] #先筛除较小的轮廓
for contour in contours:
if cv2.contourArea(contour) > minArea:
temp_contours.append(contour)
car_plates = [] #可能的车牌
for temp_contour in temp_contours:
rect = cv2.minAreaRect(temp_contour) # 该函数返回一个rect对象, rect[0]--中心点 rect[1] -- 矩形的长、宽
rect_width, rect_height = rect[1]
if rect_width < rect_height: #调整长宽
rect_width, rect_height = rect_height, rect_width
aspect_ratio = rect_width / rect_height
# 车牌正常情况下宽高比在2 - 5.5之间
if aspect_ratio > 2 and aspect_ratio < 5.5:
car_plates.append(temp_contour)
#一车只有一个车牌
if len(car_plates) == 1:
for car_plate in car_plates:
row_min, col_min = np.min(car_plate[:, 0, :], axis=0) #左上角的坐标
row_max, col_max = np.max(car_plate[:, 0, :], axis=0) #右下角的坐标
#cv2.rectangle(img, (row_min, col_min), (row_max, col_max), (0, 255, 0), 2) # 在原图中画矩形框
card_img = img[col_min:col_max, row_min:row_max, :] #把矩形框中的图像显示出来
#cv2.imshow("车牌", card_img)
return card_img
else:
print("查找车牌失败")
三、车牌字符的分割
这里用的方法和上面定位用的类似,也是使用找轮廓,设定条件筛选轮廓,显示字符,保存字符,代码如下
#字符分割部分,采用和定位类似的找轮廓的方法
def charSegment(image):
#先转为灰度图,并二值化
imgGray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #灰度
imgB = cv2.GaussianBlur(imgGray, (5, 5), 0, 0, cv2.BORDER_DEFAULT) #高斯去噪
ret, img_thresh = cv2.threshold(imgB, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 阈值分割
kernel = np.ones((3, 3), np.uint8)
img_edge = cv2.morphologyEx(img_thresh, cv2.MORPH_CLOSE, kernel)
cv2.imshow("车牌", img_edge)
contours, hierarchy = cv2.findContours(img_edge, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
char = []
i = 1
for contour in contours:
area = cv2.contourArea(contour)
print(i, "面积", area)
if area > 154:
char.append(contour)
print("选择", i)
i += 1
j = 1
for c in char:
row_min, col_min = np.min(c[:, 0, :], axis=0) # 左上角的坐标
row_max, col_max = np.max(c[:, 0, :], axis=0) # 右下角的坐标
ch = image[col_min:col_max, row_min:row_max, :] # 把矩形框中的图像显示出来
cv2.imshow(str(j), ch)
path = "char/" + str(j) + ".jpg"
cv2.imwrite(path, ch)
j += 1
四、主函数部分
if __name__ == "__main__":
#读取一张图片
srcImage=cv2.imread("car.jpg")#以灰度图形式读入
# 创建一个窗口
cv2.namedWindow("车牌")
cv2.namedWindow("原始图像")
cv2.namedWindow("定位")
srcImage = reSize(srcImage)
cv2.imshow("原始图像", srcImage)
carPic = getPostion(srcImage)#实现车牌的定位
cv2.imshow("定位", carPic)
charSegment(carPic)
# 等待任意输入
cv2.waitKey(0)
cv2.destroyAllWindows();
之后想进行改进,提高泛化能力,现在这个代码纯纯的只能用在一张图上。。。。