在经过前面我的三个文章过程,我们已经完成了车牌的定位,接下来我们需要进行字符的识别。字符识别整个过程分为两步:分
割与识别。
字符分割
我们需要将每一个字符依次输入人工神经网络进行类别甄别,从而完成识别。在得到一块车牌之后,如何将车牌中 的字符一个个分割出来。在对车牌进行了进一步去噪处理之后。我们的车牌为:
2020-11-20 22.00.27.png无论是白色区域的字母还是数字部分,都是一个整体,因此如果我们对其进行轮廓查找完全可以获得包含了待识别 的车牌字符的所有轮廓,这样就能够将对应矩形从图中抠出来得到每一个车牌字符。但是对于代表省份的汉字,他 并没有完全的连接在一起,比如:渝。如果我们对上面的车牌进行轮廓查找,将得到:
2020-11-20 22.00.38.png汉字被分割成多个轮廓,所以对于汉字的处理不能简单地进行轮廓查找。
省份字符定位
汉字代表了省份,而汉字后的字母代表了城市。城市一定跟在省份之后,那我们只要想办法得到城市的轮廓,再往 城市轮廓前扣一个矩形出来不正是省份了吗?
城市字符定位
int CarPlateRecgnize::getCityIndex(vector<Rect> src) {
int cityIndex = 0;
//循环集合
for (size_t i = 0; i < src.size(); i++) {
Rect rect = src[i];
//获得矩形
//把车牌区域划分为7个字符
//如果当前获得的矩形 它的中心点 比 1/7 大,比2/7小,那么就是城市的轮廓
int midX = rect.x + rect.width / 2;
if (midX < 136 / 7 * 2 && midX > 136 / 7) {
cityIndex = i;
break;
}
}
return cityIndex;
}
现在通过 src[cityIndex] 就能得到代表城市的字母了。
定位省份
void CarPlateRecgnize::getChineseRect(Rect city, Rect &chineseRect) {
//把宽度稍微扩大一点
float width = city.width * 1.15f;
//城市轮廓的x坐标
int x = city.x;
//x :当前汉字后面城市轮廓的x坐标
//减去城市的宽
int newX = x - width;
chineseRect.x = newX >= 0 ? newX : 0;
chineseRect.y = city.y;
chineseRect.width = width;
chineseRect.height = city.height;
}
省份轮廓矩形 chineseRect 搞定!
定位其他号码
现在我们得到了省份与城市,剩下的号码,只需要把查找到的轮廓往后再取6个即可获得所有的号码。
for (size_t i = cityIndex; i < charVec.size() && cout < 6; cout++, i++) {
plateChar.push_back(plate_shold(charVec[i]));
}
字符识别
字符的识别借助OpenCV提供的人工神经网络,识别的代码如下:
string CarPlateRecgnize::ZHCHARS[] = {"川", "鄂", "赣", "甘", "贵", "桂", "黑", "沪", "冀", "津", "京", "吉", "辽", "鲁", "蒙", "闽",
"宁", "青", "琼", "陕", "苏", "晋", "皖", "湘", "新", "豫", "渝", "粤", "云", "藏", "浙"};
char CarPlateRecgnize::CHARS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
---------
void CarPlateRecgnize::predict(vector<Mat> vec, string &result) {
for (size_t i = 0; i < vec.size(); i++) {
//提取图片特征去进行识别
Mat src = vec[i];
// imshow("a",src);
// imwrite("F:\\Lance\\OpenCV\\Car\\resource\\test\\111.jpg",src);
// waitKey();
Mat features;
//提取hog特征
getHogFeatures(annHog, src, features);
Mat response;
Point maxLoc;
//Point minLoc;
//特征置为一行
Mat samples = features.reshape(1, 1);
if (i) {
//识别字母与数字
ann->predict(samples, response);
//获取最大可信度 匹配度最高的属于31种中的哪一个。
minMaxLoc(response, 0, 0, 0, &maxLoc);
//跟你训练时候有关
int index = maxLoc.x;
result += CHARS[index];
} else {
//识别汉字
annCh->predict(samples, response);
//获取最大可信度 匹配度最高的属于31种中的哪一个。
minMaxLoc(response, 0, 0, 0, &maxLoc);
//跟你训练时候有关
int index = maxLoc.x;
//识别出来的汉字 拼到string当中去
result += ZHCHARS[index];
// cout << maxLoc.x << endl;
}
features.release();
samples.release();
}
}
这里的识别为什么这么做,需要结合训练来理解。在训练时,我们将会提取样本的特征与标签。样本特征与SVM一 样,采用HOG特征提取,同样将其置为一行;
假设我们有4个样本,分表是A、B、C的字符图片。现在我们要训练模型识别ABC字符。则首先对样本特征提取, 并记录至Mat中:
那么样本特征数据对应的标签为:
2020-11-20 22.23.01.png现在神经网络需要识别3种类别,当我们输入一个字符进行识别时,如果这个字符更接近B;那么
char CHARS[] = {'A','B','C'}
ann->predict(samples, response);
minMaxLoc(response, 0, 0, 0, &maxLoc);
int index = maxLoc.x; //得到:1
//CHARS[index] == B,识别为B
如果我们输入的字符更接近A,则得到0,则可以识别为 A 。其中 CHARS 是我们按照标签顺序定义的一个字符数 组。即通过人工神经网络识别到的标签去取对应下标的数据即可完成识别。
网友评论