Opencv2.4学习::基于形态学处理+基本特征实现车牌区域提取
基于形态学处理+基本特征实现车牌区域提取
1、形态学梯度
实际上,提取车牌还是那个思路:区域分离->轮廓检测->特征判断
这里提供这样一个算法,来源于《OpenCV图像处理编程实例》
步骤如下:
- 边缘检测,检测垂直边缘,尽量减少横向的边缘连通车牌区域----->实现手段:形态学梯度、或者Sobel边缘检测的垂直方向,当然也可以用其他边缘检测方法
- 对边缘实现二值化
- 区域填充,填补空洞----->实现手段:闭运算
- 轮廓检测,找到车牌区域的轮廓----->实现手段:findContours
- 对找到的轮廓进行遍历,根据车牌的特征(宽高比、面积比、像素等)进行筛选
- 输出
实现代码:
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace std;
using namespace cv;
Mat getPlate(int width, int height, Mat srcGray)
{
Mat result;
//形态学梯度边缘检测
//用Mat(1,2) ,用来检测出垂直的边缘
//目的:尽量减少横向的边缘连通车牌区域,或者用Sobel边缘检测的单方向检测也可以实现
/*方法1*/
morphologyEx(srcGray, result, MORPH_GRADIENT, Mat(1, 2, CV_8U, Scalar(1)));
/*方法2*/
//Mat edgeYMat;
////求y方向的Sobel边缘
//GaussianBlur(srcGray, srcGray, Size(3, 3),2);
//Sobel(srcGray, edgeYMat, CV_16S, 2, 0, 3, 1, 0, BORDER_DEFAULT);
//线性变换,转换输入数组元素为8位无符号整形
//convertScaleAbs(edgeYMat, result);
imshow("1result", result);
//二值化
threshold(result, result, 255 * 0.1, 255, THRESH_BINARY);
imshow("2,result", result);
//水平方向闭运算
//闭运算:填补空洞
if (width >= 400 && width < 600){
morphologyEx(result, result, MORPH_CLOSE,
Mat(1, 25, CV_8U, Scalar(1)));
}
else if(width>=200&&width<300){
morphologyEx(result, result, MORPH_CLOSE,
Mat(1, 20, CV_8U, Scalar(1)));
}
else if (width>=600){
morphologyEx(result, result, MORPH_CLOSE,
Mat(1, 28, CV_8U, Scalar(1)));
}
else {
morphologyEx(result, result, MORPH_CLOSE,
Mat(1, 15, CV_8U, Scalar(1)));
}
imshow("3,result", result);
//垂直方向闭运算
if (width >= 400 && width < 600){
morphologyEx(result, result, MORPH_CLOSE,
Mat(8, 1, CV_8U, Scalar(1)));
}
else if (width >= 200 && width<300){
morphologyEx(result, result, MORPH_CLOSE,
Mat(6, 1, CV_8U, Scalar(1)));
}
else if (width >= 600){
morphologyEx(result, result, MORPH_CLOSE,
Mat(10, 1, CV_8U, Scalar(1)));
}
else {
morphologyEx(result, result, MORPH_CLOSE,
Mat(4, 15, CV_8U, Scalar(1)));
}
imshow("4,result", result);
return result;
}
int main()
{
Mat srcImg = imread("F:\\opencv_re_learn\\plate.jpg");
if (!srcImg.data){
cout << "failed to read" << endl;
system("pause");
return -1;
}
Mat srcGray;
cvtColor(srcImg, srcGray, CV_BGR2GRAY);
Mat result = getPlate(400, 300, srcGray);
//连通域检测
vector<vector<Point>> blue_contours;
vector<Rect>blue_rect;
findContours(result.clone(), blue_contours, CV_RETR_EXTERNAL,
CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
//连通域遍历,车牌目标提取
for (size_t i = 0; i != blue_contours.size(); i++){
Rect rect = boundingRect(blue_contours[i]);
//矩形区域宽高比
double wh_ratio = double(rect.width) / rect.height;
//非零像素点数,即白色像素点数
int sub = countNonZero(result(rect));
//白色像素占比
double ratio = double(sub) / rect.area();
//车牌特征,条件判断
if (wh_ratio > 2 && wh_ratio < 8 && rect.height>12 &&
rect.width > 60 && ratio > 0.4){
rectangle(srcImg, rect, Scalar(0, 0, 255), 2, 8, 0);
imshow("rect", srcGray(rect));
waitKey(0);
}
}
imshow("SRCimg", srcImg);
//imshow("result", result);
waitKey(0);
}
实现效果:
上面的:
- 1result:是边缘检测的结果,大量减少了横向边缘,只保留垂直的边缘,以减少与车牌区域的联通
- 2result:二值化
- 3result:水平方向闭运算的结果,填充水平方向的空洞
- 4result:垂直方向闭运算结果,填充垂直方向的空洞
- 实现的效果也是有点惊艳,在没有用到分类器的情况下可以实现如此
当然也有局限性:
- 受角度影响
- 要指定车牌大概像素范围
- 若车牌附近有与其连通的地方,则影响效果
- 车标、或其他类矩形元素会被误认为是车牌区域