如题,我们这一篇博文的目的是为了将车牌提取出来的预处理,我将之总结为车牌特征提取。
(1)首先我们来看车牌部分与其他部分的区别,及车牌的特征,整体的车牌定位及提取方案即时基于此(基于灰度图像中考虑):
a.车牌部分是矩形
b.车牌具有特定的长宽比;
c.车牌的面积一定
(2)接着我们对整幅图片进行预处理(车牌特征提取思路,实际代码部分可能为了更好的显示效果会稍加修改)
a.图像转为灰度图,便于处理
b.图像进行平滑滤波,可以去除一定不必要细节噪声,桥接连线缝隙,主要是针对车牌矩形框,方便后面步骤中计算车牌一阶竖直梯度的效果
c,计算图像一阶竖直梯度,因为可以看到整幅图像中具有竖直线条特征的部分,除了车牌两边框外,车头部分很少有比较大块的竖直纹理的区域,这样我们就去除了许多水平方向的特征(细节),保留了竖直方向纹理的细节
d.选取合适阈值参数来进行阈值化处理
e.形态学闭操作,先膨胀后腐蚀的过程称为闭运算。它具有填充物体内细小空洞,连接邻近物体和平滑边界的作用。
这是车牌特征提取的思路,下面贴上代码及运行结果,为了选取合适参数,这里我们利用了opencv的滑动条来进行交互式的最优参数选择
下一篇博文将会就本篇博文中涉及的图像处理操作数学原理以简洁明了的语言讲述,同时对opencv部分代码进行解释
#include<opencv2\opencv.hpp>
#include<iostream>
#include<vector>
#define WINDOW_NAME "Plate recognize"
using namespace std;
using namespace cv;
//-----------------------------------【全局变量声明部分】--------------------------------------
// 描述:全局变量声明
//-----------------------------------------------------------------------------------------------
int blockSize=5;
int constValue;
Mat gaussianFilImg, sobelImg;
Mat threshImg,closeImg;
int g_nElementShape = MORPH_RECT;//元素结构的形状
//变量接收的TrackBar位置参数
int g_nMaxIterationNum = 10;
int g_nOpenCloseNum = 0;
//-----------------------------------【全局函数声明部分】--------------------------------------
Mat sobeproc(const Mat &src);
static void on_OpenClose(int, void*);//闭运算回调函数
//-----------------------------------【滑动条constValue回调】--------------------------------------
void on_Trackbar(int,void*)
{
adaptiveThreshold(sobelImg, threshImg, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY_INV, blockSize, constValue);
imshow(WINDOW_NAME, threshImg);
}
int main()
{
Mat plate = imread("H:\licenceplate\licence.bmp",0);
if (!plate.data) { cout << "error in read image please check it\n";return false; }
GaussianBlur(plate, gaussianFilImg, Size(5,5),0,0);
sobelImg= sobeproc(gaussianFilImg);
//adaptiveThreshold(sobelImg, threshImg, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY_INV, blockSize, constValue);
constValue = 10;//自适应阈值化参数
g_nOpenCloseNum = 9;//闭运算参数
namedWindow(WINDOW_NAME,1);
createTrackbar("threshold",WINDOW_NAME,&constValue,100,on_Trackbar);
createTrackbar("迭代值", WINDOW_NAME, &g_nOpenCloseNum, g_nMaxIterationNum *10 + 1, on_OpenClose);
//执行回调函数
on_Trackbar(constValue, 0);
on_OpenClose(g_nOpenCloseNum, 0);
while (1)
{
int c = waitKey(0);
if((char)c==' ')
{
vector<vector<Point>>contours;
findContours(threshImg, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
drawContours(threshImg, contours, -1, Scalar(26, 24, 46), 3);
}
break;
}
waitKey();
return 0;
}
//sobel算子求得一阶水平方向导数,以此求垂直边缘
Mat sobeproc(const Mat &src)
{
Mat dst;
Mat grad_x, grad_y,abs_grad_x,abs_grad_y;
Sobel(src, grad_x, CV_8U, 1, 0, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(grad_x, abs_grad_x);
Sobel(src, grad_y, CV_8U, 0,1, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(grad_y, abs_grad_y);
addWeighted(abs_grad_x,0.6, abs_grad_y, 0.4, 0, dst);
return dst;
}
//-----------------------------------【on_OpenClose( )函数】----------------------------------
// 描述:【开运算/闭运算】窗口的回调函数
//-----------------------------------------------------------------------------------------------
static void on_OpenClose(int, void*)
{
//偏移量的定义
int offset = g_nOpenCloseNum - g_nMaxIterationNum;//偏移量
int Absolute_offset = offset > 0 ? offset : -offset;//偏移量绝对值
//自定义核
Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset * 2 + 1,
Absolute_offset * 2 + 1), Point(Absolute_offset, Absolute_offset));
if (offset < 0)
morphologyEx(threshImg,closeImg,MORPH_OPEN, element);
else
morphologyEx(threshImg, closeImg, MORPH_CLOSE, element);
imshow(WINDOW_NAME, closeImg);
}