前一篇讲解到了将用蓝色筛选后的图片,再一次灰阶/二值化。现在从这里继续讲解。
因为车牌是一个矩形。所以接着将又一次二值化之后的图片,进行膨胀,之后在进行矩形检测。框选出可能是车牌号的矩形区域。 代码如下:
int** car_License_box(Mat& mat1, Mat& mat2, int* number){
Mat threshold_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
Point s1, s2;
int width_1, height_1;
int width = mat1.rows;
int height = mat1.cols;
int sum = 0;
int morph_elem = 3;
int morph_size = 3;
int** a = (int**)malloc(width * sizeof(int*));
//腐蚀
Mat element = getStructuringElement(MORPH_RECT, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( -1, -1));
dilate(mat1, mat1, element);
/// 找到轮廓
findContours(mat1, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
/// 多边形逼近轮廓 + 获取矩形和圆形边界框
vector<vector<Point> > contours_poly( contours.size() );
vector<Rect> boundRect( contours.size() );
vector<Point2f>center( contours.size() );
vector<float>radius( contours.size() );
for( int i = 0; i < contours.size(); i++ )
{ approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
boundRect[i] = boundingRect( Mat(contours_poly[i]) );
minEnclosingCircle( contours_poly[i], center[i], radius[i] );
}
/// 画多边形轮廓 + 包围的矩形框 + 圆形框
mat2 = Mat::zeros(mat1.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
s1 = boundRect[i].tl();
s2 = boundRect[i].br();
height_1 = s2.x - s1.x;
width_1 = s2.y - s1.y;
if((height_1 > (3 * width_1)) && (width_1 > (width / 2))){
a[sum] = (int* )malloc(4 * sizeof(int));
a[sum][0] = s1.x;
a[sum][1] = s1.y;
a[sum][2] = s2.x;
a[sum][3] = s2.y;
sum += 1;
}
}
*number = sum;
return a;
}
int main(int argc, char **argv){
.............
pic_gray(img_3, img_3);
threshold = histogram_Calculate(img_3, 3);
pic_Thresholding(img_3, threshold);
address_1 = car_License_box(img_3, img_4, &address_Number_1);
sprintf(str, "%d", i);
namedWindow(str);
imshow(str, img_3);
}
}
waitKey(0);
return 0;
}
在函数car_License_box中,img_3为传入的源图像,然后检测出合适矩形,将矩形左上和右下那个角点保存在二维数组address_1中,检测到的矩形数量保存在
address_Number_1中。
该函数的具体流程如下:1、使用dilate来进行图像的膨胀。
2、使用opencv教程中讲解过的轮廓寻找,并将检测到的矩形角点保存在boundRect中。
3、根据矩形左上和右下的角点,可以计算出矩形的宽与高。车牌所在的矩形应该长至少是宽的三倍。
同时在之前分割的图片中,车牌的宽度至少应该是这个分割图片的一半以上才正常。
4、将满足了要求的矩形角点保存在二维数组a中,并将矩形探测计数+1,最后返回对应保存的二维数组。
上面矩形分割,已经将可能是车牌的矩形位置角点保存在了address_1中,这里我们根据address_1中的角点坐标,将对应的矩形从原图像中复制到新图像中显示:
void pic_cutting_1(Mat& mat1, Mat& mat2, Point s1, Point s2){
int i, j;
IplImage pI_1;
IplImage pI_2;
CvScalar s;
mat2 = cv::Mat(s2.y - s1.y, s2.x - s1.x, CV_8UC3, 1);
pI_1 = mat1;
pI_2 = mat2;
for(i = s1.y; i < s2.y; i++){
for(j=s1.x; j<s2.x; j++){
s = cvGet2D(&pI_1, i, j);
cvSet2D(&pI_2, i-s1.y, j-s1.x, s);
}
}
}
int main(int argc, char** argv){
.....................
address_1 = car_License_box(img_3, img_4, &address_Number_1);
for(j=0; j< address_Number_1; j++){
DE("address_0:%d, %d, %d, %d\n", address_1[j][0], address_1[j][1], address_1[j][2], address_1[j][3]);
s1.y = address_1[j][1] + selection_1[i][0];
s1.x = address_1[j][0];
s2.y = address_1[j][1] + selection_1[i][1];
s2.x = address_1[j][2];
DE("address:%d, %d, %d, %d\n", s1.x, s1.y, s2.x, s2.y);
pic_cutting_1(img, img_5, s1, s2);
sprintf(str, "%d", j);
namedWindow(str);
imshow(str, img_5);
}
}
}
namedWindow("img");
imshow("img",img);
waitKey(0);
return 0;
}
这一步很简单,效果显示如下:
在上面的效果图片中,我们看到现在探测出了两个可能是车牌的矩形位置。继续做一次筛选判断来确定车牌真正所在的位置。
int box_selection(Mat& mat1){
int width_1, height_1;
int width = mat1.rows;
int height = mat1.cols;
int i, j;
IplImage pI_1 = mat1;
CvScalar s;
int find_blue = 0;
int blueToWhite = 0;
int sum =0;
for(i=0; i<width; i++){
find_blue = 0;
blueToWhite = 0;
for(j=0; j<height; j++){
s = cvGet2D(&pI_1, i, j);
if((s.val[0] - s.val[1] > 10) && (s.val[0] - s.val[2] > 10) && (s.val[1] < 150) && (s.val[2] < 150)){
find_blue = 1;
}
else if((s.val[1] > 150) && (s.val[2] > 150) && (s.val[0] > 150) && (find_blue == 1)){
blueToWhite += 1;
find_blue = 0;
}
}
if(blueToWhite > 5){
sum += 1;
}
}
return sum;
}
int main(int argc, char **argv){
............
for(j=0; j< address_Number_1; j++){
DE("address_0:%d, %d, %d, %d\n", address_1[j][0], address_1[j][1], address_1[j][2], address_1[j][3]);
s1.y = address_1[j][1] + selection_1[i][0];
s1.x = address_1[j][0];
s2.y = address_1[j][1] + selection_1[i][1];
s2.x = address_1[j][2];
DE("address:%d, %d, %d, %d\n", s1.x, s1.y, s2.x, s2.y);
pic_cutting_1(img, img_5, s1, s2);
box_flag = box_selection(img_5);
DE("box_flag:%d\n", box_flag);
if(box_flag > 5){
rectangle(img, s1, s2, color, 2, 8, 0 );
sprintf(str, "%d", j);
namedWindow(str);
imshow(str, img_5);
}
}
}
}
namedWindow("img");
imshow("img",img);
waitKey(0);
return 0;
}
因为车牌号是蓝底白字,同时会交替出现7次。所以在box_selection,我们在蓝色之后,跳变到白色的次数至少大于五次,表示该矩形位置是框选了车牌。
来排除掉其他的矩形,最后将确定的车牌位置新图像显示出来,同时在原图像车牌的位置画上黄色方框。
进而最后的效果演示如下:
该方法的准确率并不太高。如可能出现如下情况:

代码下载如下:http://download.csdn.net/detail/u011630458/8431445
原文地址:http://blog.csdn.net/u011630458/article/details/43604737