目标: 提取车牌所在的区域
PS:现在还没学习文字检测,以后再来补充~~
思路:
1.利用形态学+梯度+轮廓检测,但是这个形态学要求比较高,得调节到适当的参数。
2.利用HSV颜色分离+梯度+形态学+轮廓检测,这个方法的适应度比较高,行比较上面的方法较难理解。
本文使用第二种方法实现:
PS:不要问为什么用这种方法,没有为什么的方法,只有解决问题的方法,手动操作一下就懂了。
先上效果图:
梯度检测图(1-1)
S空间图(1-2)
S空间分离蓝色图(1-3)
(HSV图像分离+梯度)结合图(1-4)
轮廓检测处理图(1-5)
最终效果图(1-6)
上代码:
1 #if 1 2 #include <opencv2/opencv.hpp> 3 #include <iostream> 4 #include "math.h" 5 6 using namespace cv; 7 using namespace std; 8 9 int main(int argc, char**argv) 10 { 11 Mat input_image; 12 input_image = imread("car.jpg"); 13 if (input_image.data == NULL) { 14 return -1; cout << "can't open image.../"; 15 } 16 //-----------------------------------------------------------------------------------// 17 //------------------------------梯度检测图像--------------------------------------// 18 //-----------------------------------------------------------------------------------// 19 Mat input_image1; 20 input_image.copyTo(input_image1); 21 cvtColor(input_image1, input_image1, CV_BGR2GRAY); 22 input_image1.convertTo(input_image1, CV_32FC1); 23 Mat sobelx = (Mat_<float>(3, 3) << -0.125, 0, 0.125, -0.25, 0, 0.25, -0.125, 0, 0.125); 24 filter2D(input_image1, input_image1, input_image1.type(), sobelx); 25 Mat mul_image; 26 multiply(input_image1, input_image1, mul_image); 27 const int scaleValue = 4; 28 double threshold = scaleValue * mean(mul_image).val[0];//4 * img_s的平均值 29 Mat resultImage = Mat::zeros(mul_image.size(), mul_image.type()); 30 float* pDataimg_s = (float*)(mul_image.data); 31 float* pDataresult = (float*)(resultImage.data); 32 const int height = input_image1.rows; 33 const int width = input_image1.cols; 34 //--- 非极大值抑制 + 阈值分割 35 for (size_t i = 1; i < height-1; i++) 36 { 37 for (size_t j = 1; j < width-1; j++) 38 { 39 bool b1 = (pDataimg_s[i*height + j] > pDataimg_s[i*height + j - 1]); 40 bool b2 = (pDataimg_s[i*height + j] > pDataimg_s[i*height + j + 1]); 41 bool b3 = (pDataimg_s[i*height + j] > pDataimg_s[(i - 1)*height + j]); 42 bool b4 = (pDataimg_s[i*height + j] > pDataimg_s[(i + 1)*height + j]); 43 pDataresult[i*height + j] = 255 * ((pDataimg_s[i*height + j] > threshold) && ((b1&&b2) || (b3&&b4))); 44 } 45 } 46 resultImage.convertTo(resultImage, CV_8UC1); 47 //-----------------------------------------------------------------------------------// 48 //---------------------------------HSV通道提取---------------------------------------// 49 //-----------------------------------------------------------------------------------// 50 Mat input_image2; 51 input_image.copyTo(input_image2); 52 Mat img_h, img_s, img_v, img_hsv; 53 cvtColor(input_image2, img_hsv, CV_BGR2HSV); 54 vector<Mat> hsv_vec; 55 split(img_hsv, hsv_vec); 56 img_h = hsv_vec[0]; 57 img_s = hsv_vec[1]; 58 img_v = hsv_vec[2]; 59 img_h.convertTo(img_h, CV_32F); 60 img_s.convertTo(img_s, CV_32F); 61 img_v.convertTo(img_v, CV_32F); 62 normalize(img_h, img_h, 0, 1, NORM_MINMAX); 63 normalize(img_s, img_s, 0, 1, NORM_MINMAX); 64 normalize(img_v, img_v, 0, 1, NORM_MINMAX); 65 //----下面的操作等同上面的归一化--------// 66 //double h_max, s_max, v_max; 67 ////minMaxIdx(img_h, 0, &h_max); 68 //minMaxLoc(img_h, 0, &h_max); 69 //minMaxLoc(img_s, 0, &s_max); 70 //minMaxLoc(img_v, 0, &v_max); 71 //img_h /= h_max; 72 //img_s /= s_max; 73 //img_v /= v_max; 74 Mat img_vblue = ((img_h > 0.45)&(img_h < 0.75)&(img_s > 0.15)&(img_v > 0.25));//蓝色通道提取 75 Mat vbule_gradient = Mat::zeros(input_image2.size(), CV_8UC1); 76 for (size_t i = 1; i < height-1; i++) 77 { 78 for (size_t j = 1; j < width-1; j++) 79 { 80 /*Rect rec; 81 rec.x = j - 1; 82 rec.y = i - 1; 83 rec.width = 3; 84 rec.height = 3;*/ 85 //----梯度和蓝色区域重合的部分,也可以用上面的矩形3X3的判断 86 vbule_gradient.at<uchar>(i, j) = (resultImage.at<uchar>(i, j) == 255 && img_vblue.at<uchar>(i,j) != 0) ? 255 : 0; 87 //vbule_gradient.at<uchar>(i, j) = (resultImage.at<uchar>(i, j) == 255 && countNonZero(img_vblue(rec)) >= 1) ? 255 : 0; 88 } 89 } 90 //-----------------------------------------------------------------------------------// 91 //-----------------------------形态学+轮廓提取车牌-----------------------------------// 92 //-----------------------------------------------------------------------------------// 93 Mat morph; 94 morphologyEx(vbule_gradient, morph, MORPH_CLOSE, Mat::ones(2, 25, CV_8UC1)); 95 vector<vector<Point>> contours; 96 vector<Vec4i> hierarchy; 97 findContours(morph, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1)); 98 Rect rec_adapt; 99 for (size_t i = 0; i < contours.size(); i++) 100 { 101 //----矩形区域非零像素占总的比例,防止有较大的空白区域干扰检测结果 102 //----矩形的长宽限制,也可以再增加额外条件:长宽比例等 103 int true_pix_count = countNonZero(morph(boundingRect(contours[i]))); 104 double true_pix_rate = static_cast<double>(true_pix_count) / static_cast<double>(boundingRect(contours[i]).area()); 105 if (boundingRect(contours[i]).height > 10 && boundingRect(contours[i]).width > 80 && true_pix_rate > 0.7) 106 { 107 rec_adapt = boundingRect(contours[i]); 108 drawContours(morph, contours, static_cast<int>(i), Scalar(255, 255, 255), 2); 109 } 110 } 111 imshow("Area Brand", input_image(rec_adapt)); 112 waitKey(0); 113 return 0; 114 } 115 #endif
参考:《opencv图像处理编程实例》