基于vs2015+opencv3.3的简易的车牌定位
直接上代码
#include<opencv2\opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int areas;
//该函数用来验证是否是我们想要的区域,车牌定位原理其实就是在图片上寻找矩形,我们可以用长宽比例以及面积来验证是否是我们想要的矩形,宽高比为520/110=4.7272 (车牌的长除以宽),区域面积最小为15个像素,最大为125个像素
bool VerifySize(RotatedRect candidate) {
float error = 0.4; //40%的误差范围
float aspect = 4.7272;//宽高比例
int min = 15 * aspect * 15; //最小像素为15
int max = 125 * aspect * 125;//最大像素为125
float rmin = aspect - aspect*error;//最小误差
float rmax = aspect + aspect*error;//最大误差
int area = candidate.size.height*candidate.size.width;//求面积
float r = (float)candidate.size.width / (float)candidate.size.height;//长宽比
if (r < 1)
r = 1 / r;
if (area<min || area>max || r<rmin || r>rmax)
return false;
else
return true;
}
int main(int argc, char** argv) {
Mat src;
src = imread("D:\\Car.jpg");//读取含车牌的图片
if (!src.data)
{
cout << "Could not open Car.jph.." << endl;
return -1;
}
Mat img_gray;
cvtColor(src, img_gray, CV_BGR2GRAY);//灰度转换
Mat img_blur;
blur(img_gray, img_blur, Size(5, 5));//用来降噪
Mat img_sobel;
Sobel(img_gray, img_sobel, CV_8U, 1, 0, 3);//Sobel滤波,对x进行求导,就是强调y方向,对y进行求导,就是强调x方向,在此我们对x求导,查找图片中的竖直边
Mat img_threshold;
threshold(img_sobel, img_threshold, 0, 255, THRESH_BINARY | THRESH_OTSU);
Mat element = getStructuringElement(MORPH_RECT, Size(21, 5));//这个Size很重要!!不同的图片适应不同的Size,待会在下面放图,大家就知道区别了
morphologyEx(img_threshold, img_threshold,MORPH_CLOSE,element);//闭操作,就是先膨胀后腐蚀,目的就是将图片联通起来,取决于element的Size。
/*接下来就是提取轮廓*/
vector<vector<Point>>contours;
findContours(img_threshold, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
Mat result = Mat::zeros(src.size(), CV_8U);
drawContours(result, contours, -1, Scalar(255));
vector<RotatedRect> rects; //用来存放旋转矩形的容器
//Mat result1 = Mat::zeros(src.size(), CV_8U);
Mat result1;
src.copyTo(result1);
for (size_t i = 0; i < contours.size(); i++)
{
Point2f vertices[4];//用来存放旋转矩形的四个点
RotatedRect mr = minAreaRect(Mat(contours[i]));
//minAreaRect 寻找最小的矩形
if (VerifySize(mr))//筛选是否是我们需要的区域,如果验证成功,就放到rects里,
{
//if (mr.angle > -30) {
mr.points(vertices);
for (size_t j = 0; j < 4; j++)
{
line(result1, vertices[j], vertices[(j + 1) % 4], Scalar(0, 0, 255), 2, 8);
cout << "矩形坐标"<<j<<"为" << vertices[j] << endl;
}
cout << "height:" << mr.size.height << endl << "weight:" << mr.size.width << endl;
rects.push_back(mr);
cout << "矩形角度:" << mr.angle << endl;
// }
}
}
vector<Mat>output;//用于存放识别到的图像
for (size_t i = 0; i < rects.size(); i++)
{
Mat dst_warp;
Mat dst_warp_rotate;
Mat rotMat(2, 3, CV_32FC1);
dst_warp = Mat::zeros(src.size(), src.type());
float r = (float)rects[i].size.width / (float)rects[i].size.height;
float angle = rects[i].angle;
if (r < 1)
angle = angle + 90;
rotMat = getRotationMatrix2D(rects[i].center,angle, 1);//其中的angle参数,正值表示逆时针旋转,关于旋转矩形的角度,以为哪个是长哪个是宽,在下面会说到
warpAffine(src, dst_warp_rotate, rotMat, dst_warp.size());//将矩形修正回来
Size rect_size = rects[i].size;
if (r < 1)
swap(rect_size.width, rect_size.height);
Mat dst(rects[i].size, CV_8U);
getRectSubPix(dst_warp_rotate, rect_size, rects[i].center, dst);//裁剪矩形
/*以下代码是将裁减到的矩形设置为相同大小,并且提高对比度*/
Mat resultResized;
resultResized.create(33, 144, CV_8UC3);
resize(dst, resultResized, resultResized.size(), 0, 0, INTER_CUBIC);
Mat grayResult;
cvtColor(resultResized, grayResult, CV_BGR2GRAY);
blur(grayResult, grayResult, Size(3, 3));
equalizeHist(grayResult, grayResult); //均值化提高对比度
output.push_back(grayResult); //存放图片
}
char name[20] = "";
for (size_t i = 0; i < output.size(); i++)
{
sprintf_s(name, "识别到的第%d个车牌", i+1);
imshow(name, output[i]);
}
waitKey(0);
return 0;
}
现在来说一下element的Size的问题,我们要的是将车牌区域连通,我们来看一下9*3的Size
再来看一下17*3的情况
我们来看一下 车牌区域连通在一起了 ,这就是我们所需要的Size了
PS:不同的图片需要修改不同的Size 这个问题我还没解决。。
现在来说下旋转矩形的问题,左上角为原点,水平方向为x,竖直方向为y方向
angle为x轴逆时针旋转 先碰到的第一个边所成的夹角(夹角范围-90,0),并且该边为width ,我们附加一张图片就可以很清楚了
上面有段代码是
if (r < 1)
angle = angle + 90; 大家结合上面那张图想想,假设是-89度,并且r是<1的 ,我们如果没有这个angle+90,在 getRotationMatrix2D方法中旋转-89度,他会变成垂直,如果加上90,就变成1度,他旋转1度,就修正成水平了。 接下来放图。
灰度转换
滤波
二值化
提取轮廓
红色框出我们得到的车牌
最终显示的图片
其实车牌识别就是寻找矩形+SVM分类..,前期简单的矩形识别已经完成了,就是结合SVM分类器进行筛选了。