前一篇讲解到了将用蓝色筛选后的图片,再一次灰阶/二值化。现在从这里继续讲解。
因为车牌是一个矩形。所以接着将又一次二值化之后的图片,进行膨胀,之后在进行矩形检测。框选出可能是车牌号的矩形区域。 代码如下:
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