有写过两篇文章介绍如何检测车牌及对车牌字符进行识别,写的有些凌乱,这篇文章将整合一个较为完整的、简短的思路。
1.车牌检测
主要的步骤:
a. sobel滤波器
b. 阈值算子
c. 闭形态学算子
d. 一个区域填充掩码
e. 用红色标记可能存在的车牌区域
f. 执行SVM分类检测车牌
a~e属于图像分割,f属于模式识别内容。在车牌分割时,使用的是查找轮廓的方法,提取外接矩形区域,对矩形区域进行车牌的判断(利用车牌的一些先验知识:宽高比、区域像素比等),提取符合要求的可能是车牌的区域,再进行SVM判断。SVM识别的输入图像应该具有一样的大小。
2.车牌字符识别
将使用人工神经网络对检测出的车牌进行字符识别。
主要步骤:
a. OCR(光学字符)分割(直方图均衡 ->阈值滤波 -> 查找轮廓 ->验证轮廓是否为字符)
b. 特征提取(水平和竖直累积直方图、低分辨率图像)
c. OCR分类(使用人工神经网络ANN)
字符的分割与车牌区域的分割有点类似, 通过对检测的车牌进行图像分割,检测出可能存在字符的区域,同样根据车牌字符的先验知识(字符宽高比、字符区域像素比)判断字符。
3.车牌类
为了管理车牌,定义一个车牌类。该类记录车牌字符的相对位置,实现最后车牌的输出位置。之前没有用到,现在给出:
class Plate
{
public:
Plate();
Plate (Mat img, Rect pos);
string str();
Rect position;
Mat plateImg;
vector< char > chars;
vector < Rect> charsPos;
};
Plate::Plate()
{
}
string Plate::str()
{
string result="";
//Order numbers
vector<int> orderIndex;
vector<int> xpositions;
for(int i=0; i< charsPos.size(); i++){
orderIndex.push_back(i);
xpositions.push_back(charsPos[i].x);
}
float min=xpositions[0];
int minIdx=0;
for(int i=0; i< xpositions.size(); i++){
min=xpositions[i];
minIdx=i;
for(int j=i; j<xpositions.size(); j++){
if(xpositions[j]<min){
min=xpositions[j];
minIdx=j;
}
}
int aux_i=orderIndex[i];
int aux_min=orderIndex[minIdx];
orderIndex[i]=aux_min;
orderIndex[minIdx]=aux_i;
float aux_xi=xpositions[i];
float aux_xmin=xpositions[minIdx];
xpositions[i]=aux_xmin;
xpositions[minIdx]=aux_xi;
}
for(int i=0; i<orderIndex.size(); i++){
result=result+chars[orderIndex[i]];
}
return result;
}
检测的结果: