``# 车牌识别之定位篇
/*车牌定位:
读取图片-- > 高斯模糊-- >
灰度化-- > Sobel算子边缘检测-- >
二值化-- > 闭操作-- >
膨胀腐蚀-- > 矩形轮廓查找-- >
背景分割(grabCut)—— > 字符分割—— >
字符识别-- >
*/
#include<opencv2/opencv.hpp>
#include"iostream"
#include<opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include"opencv2/highgui/highgui.hpp"
using namespace std;
using namespace cv;
int main(int argc, char* argv[], char** env)
{//读取图像
Mat img = imread("12.jpg");
if (img.empty())
return 0;
//高斯模糊
Mat blurr_image;
GaussianBlur(img, blurr_image, Size(3, 3), 0, 0);
//灰度化图像
Mat gray_image;
cvtColor(blurr_image, gray_image, CV_RGB2GRAY);
//sobel算子
Mat gx, gy;
Mat abs_gx, abs_gy, margin_image;
//求x方向梯度
Sobel(gray_image, gx, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(gx, abs_gx);
`
```python
``
//求y方向梯度
Sobel(gray_image, gy, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(gy, abs_gy);
//合并梯度
addWeighted(abs_gx, 0.5, abs_gy, 0.5, 0, margin_image);
//二值化图像
int threshval = 170;//设定阈值
Mat bin;
bin = (threshval < 100) ? (abs_gx < 160) : (abs_gx > 160);//二值化
//namedWindow("二值化后的图像",0);
//imshow("二值化后的图像", tn);
//形态学处理
//闭操作将目标区域连成一个整体,便于后续轮廓的提取
Mat dst;
//定义核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
morphologyEx(bin, dst, MORPH_CLOSE, element);
//图片膨胀处理
Mat dilate_image, erode_image;
//自定义 核进行 x 方向的膨胀腐蚀
Mat elementX = getStructuringElement(MORPH_RECT, Size(24, 1));
Mat elementY = getStructuringElement(MORPH_RECT, Size(1, 20));
Point point(-1, -1);
//膨胀
dilate(dst, dilate_image, elementX, point, 5);
//腐蚀
erode(dilate_image, erode_image, elementX, point, 8);
dilate(erode_image, dilate_image, elementX, point, 6);
//自定义 核进行 Y 方向的膨胀腐蚀
erode(dilate_image, erode_image, elementY, point, 4);
dilate(erode_image, dilate_image, elementY, point, 4);
//取轮廓
//矩形轮廓查找与筛选:
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
RNG rng;//随机数生成
findContours(dilate_image, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
Scalar color = Scalar(0, 255, 0);
int i = 0;
CvRect aRect = boundingRect(contours[i]);
for (int i = 0; i < contours.size(); i++)
{
//使用边界框的方式 ,计算轮廓的垂直边界最小矩形,矩形是与图像上下边界平行的
//CvRect aRect = boundingRect(contours[i]);
int tmparea = aRect.height*aRect.height;
if (((double)aRect.width / (double)aRect.height > 2.7) && ((double)aRect.width / (double)aRect.height < 6) && tmparea >= 100 && tmparea <= 25000)
{
rectangle(img, cvPoint(aRect.x, aRect.y), cvPoint(aRect.x + aRect.width, aRect.y + aRect.height), color, 4);
}
}
namedWindow("contour", 0);
resizeWindow("contour", 900, 800);
//显示最终图像
imshow("contour", img);
Mat roi1;
Rect rect1(aRect.x, aRect.y, aRect.width, aRect.height);
img(rect1).copyTo(roi1);
//namedWindow("last", 0);
resizeWindow("last", 1000, 250);
imshow("last", roi1);
//字符分割
Mat Gray_Carimage;
//pic_gray(roi1, Gray_Carimage);
cvtColor(roi1, Gray_Carimage, CV_RGB2GRAY);
/*Mat Candy_Carimage;
Canny(roi1, Candy_Carimage, 450, 120, 3);*/
resizeWindow("cargrayimage", 1000, 250);
imshow("cargrayimage", Gray_Carimage);
Mat roi_threadhold_image;
threshold(Gray_Carimage, roi_threadhold_image, 0, 255, CV_THRESH_OTSU);
resizeWindow("test", 1000, 250);
imshow("test", roi_threadhold_image);
//****倾斜矫正
//****去除边框
//**************************去除边框
//水平投影和垂直投影
int imgR[100] = { 0 };
int imgTop = 0; int imgBottom = 0;
for (int ht = 0; ht < roi_threadhold_image.rows; ht++)
{
for (int wt = 0; wt < roi_threadhold_image.cols; wt++)
{
if (roi_threadhold_image.at<uchar>(ht, wt) != 0)
{
imgR[ht]++;
}
}
if ( imgR[ht] > 10)
{
imgTop = ht;
break;
}
}
for (int ht = roi_threadhold_image.rows - 1; ht > 0; ht--)
{
for (int wt = 0; wt < roi_threadhold_image.cols; wt++)
{
if (roi_threadhold_image.at<uchar>(ht, wt) == 255)
{
imgR[ht]++;
}
}
if (imgR[ht] > 10)
{
imgBottom = ht;
break;
}
}
int imgR_w[350] = { 0 };
int imgRight = 0;
int imgLeft = 0;
for (int wt_new = 2; wt_new < roi_threadhold_image.cols; wt_new++)
{
for (int ht_new = imgTop + 15; ht_new < imgBottom; ht_new++)
{
if (255 == roi_threadhold_image.at<uchar>(ht_new, wt_new))
{
imgR_w[wt_new]++;
}
}
if ((imgR_w[wt_new - 2] + imgR_w[wt_new - 1] + imgR_w[wt_new]) > 10)
{
imgLeft = wt_new;
break;
}
}
for (int wt_new = roi_threadhold_image.cols - 3; wt_new > 0; wt_new--)
{
for (int ht_new = imgTop + 15; ht_new < imgBottom; ht_new++)
{
if (255 == roi_threadhold_image.at<uchar>(ht_new, wt_new))
{
imgR_w[wt_new]++;
}
}
if ((imgR_w[wt_new] + imgR_w[wt_new + 1] + imgR_w[wt_new + 2]) > 10)
{
imgRight = wt_new - 3;
break;
}
}
Mat roi_fix;
roi_fix = roi_threadhold_image(Rect(imgLeft + 5, imgTop + 8, imgRight - (imgLeft + 5), (imgBottom - (imgTop + 10))));
//去除字符串上下边界外的区域
GaussianBlur(roi_fix, roi_fix, Size(3, 3), 3.0);
threshold(roi_fix, roi_fix, 0, 255, CV_THRESH_OTSU);
int a[100] = { 0 };
int b[350] = { 0 };
int imgT = 0;
int imgB = 0;
for (int ht = 0; ht < roi_fix.rows; ht++)
{
for (int wt = 0; wt < roi_fix.cols; wt++)
{
if (roi_fix.at<uchar>(ht, wt) == 255)
{
a[ht]++;
}
}
if (a[ht] > 30)
{
imgT = ht;
break;
}
}
for (int ht = roi_fix.rows - 1; ht > 0; ht--)
{
for (int wt = 0; wt < roi_fix.cols; wt++)
{
if (roi_fix.at<uchar>(ht, wt) == 255)
{
b[ht]++;
}
}
if (b[ht] > 20)
{
imgB = ht;
break;
}
}
Mat roi_fix2 = roi_fix(Rect(0, imgT, roi_fix.cols, imgB - imgT));
resizeWindow("roi", 1000, 255);
imshow("roi", roi_fix2);
cout << "roi rows: " << roi_fix2.rows << " roi cols: " << roi_fix2.cols << endl;
//****分割字符
Mat roi_bi = roi_fix2;
cout << "row:" << roi_fix2.rows << " cols: " << roi_fix2.cols << endl;
bool lab = false; //是否进入一个字符分割状态
bool black = false; //是否发现黑点
bool change = false;
int xnum = 0;
int rect_left;
int rect_right;
Mat fg1, fg2, fg3, fg4, fg5, fg6, fg7,fg8;
for (int wt = 0; wt < roi_bi.cols; wt++)
{
int count = 0;
for (int ht = 0; ht < roi_bi.rows; ht++)
{
if ((255 == roi_bi.at<uchar>(ht, wt)) )
{
count++;
}
}
if (!lab && (count > 5))
{
rect_left = wt ;
lab = true;
}
if (lab && (count < 5) && (wt > (rect_left + 20)) && (xnum < 7))
{
rect_right = wt;
lab = false;
CvPoint pt1, pt2;
pt1.x = rect_left;
pt1.y = 0;
pt2.x = rect_right;
pt2.y = roi_bi.cols - 1;
int s_x = pt1.x ;
int s_y = pt1.y;
int s_width = rect_right - rect_left + 1;
int s_height = roi_bi.rows - 1;
if (xnum == 0)
{
fg1 = roi_bi(Rect(s_x, s_y, s_width, s_height));
}
if (xnum == 1)
{
fg2 = roi_bi(Rect(s_x, s_y, s_width, s_height));
}
if (xnum == 2)
{
fg3 = roi_bi(Rect(s_x, s_y, s_width, s_height));
}
if (xnum == 3)
{
fg4 = roi_bi(Rect(s_x, s_y, s_width, s_height));
}
if (xnum == 4)
{
fg5 = roi_bi(Rect(s_x, s_y, s_width, s_height));
}
if (xnum == 5)
{
fg6 = roi_bi(Rect(s_x, s_y, s_width, s_height));
}
if (xnum == 6)
{
fg7 = roi_bi(Rect(s_x, s_y, s_width, s_height));
}
if (xnum == 7)
{
fg8 = roi_bi(Rect(s_x, s_y, s_width, s_height));
}
xnum++;
}
}
imshow("1", fg1); imshow("2", fg2); imshow("3", fg3);
imshow("4", fg4); imshow("5", fg5); imshow("6", fg6);
imshow("7", fg7); imshow("8", fg8);
waitKey(0);
return 0;
}
版权声明:本文为l315676原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。