基于SVM和神经网络的车牌识别(三)
本系列文章参考自《深入理解OpenCV实用计算机视觉项目解析》仅作学习用途
车牌号提取
本篇用到的原始图像为:
首先,对车牌图像用直方图均衡化处理,相当于提高了图像的对比度。
// 车牌号识别
Mat src = imread("2715DTZ.jpg", 0);
// 直方图均衡化
equalizeHist(src, src);
imshow("【均衡化后的灰度图】", src);
然后,对图像反转阈值化,把黑色区域变为白色,白色区域变为黑色
// CV_THRESH_BINARY_INV 当前点值大于60时,设置为0,否则设置为255
threshold(src, src, 60, 255, CV_THRESH_BINARY_INV);
imshow("【阈值化后的图像】", src);
在阈值化后,进行膨胀操作,目的是把数字显示得更加清楚,这一步是书上没有的
//膨胀操作
Mat element = getStructuringElement(0, Size(3, 3));
Mat dst;
dilate(src, dst, element);
imshow("【膨胀后的图像】", dst);
下一步进行轮廓检测,对检测到的所有轮廓,通过大小、宽高比等信息,删除那些不正确的轮廓
这一步与OpenCV自学笔记17. 基于SVM和神经网络的车牌识别(一)中的验证思想类似
// 为了更好地绘制出轮廓,按照BGR三通道从新读取图片,这样就可以画其他颜色了
Mat copy = imread("2715DTZ.jpg");
vector<vector<Point>> contours;
// CV_RETR_EXTERNAL 只检测外轮廓
// CV_CHAIN_APPROX_NONE 存储所有轮廓点
findContours(dst, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
for (int i = 0; i < contours.size(); i++) {
drawContours(copy, contours, i, Scalar(0, 255, 0), 1); // 绘制轮廓
Rect rect = boundingRect(contours[i]);
rectangle(copy, rect, Scalar(0,0,255), 1);
Mat roi(src, rect);
if (verifyLetter(roi)) {
// 绘制通过验证的矩形
rectangle(copy, rect, Scalar(255, 0, 0), 2);
// 保存图像
imwrite( to_string(i) + ".jpg", roi);
}
}
imshow("【绘制轮廓】", copy);
结果见下图,蓝色的是通过验证的矩形
程序运行后,在目录下能看到切割后字母:
下面是验证程序,套路都是一样的
/* 验证字母 */
bool verifyLetter(Mat r) {
const float aspect = 45.0f / 77.0f;
float charAspect = (float)r.cols / (float)r.rows;
float error = 0.35;
float minHeight = 15;
float maxHeight = 28;
float minAspect = 0.2;
float maxAspect = aspect + aspect * error;
float area = countNonZero(r);
float bbArea = r.cols * r.rows;
float percPixels = area / bbArea;
return percPixels < 0.8 &&
charAspect > minAspect &&
charAspect < maxAspect &&
r.rows >= minHeight && r.rows <= maxHeight;
}
到此为止,本篇的全部代码如下:
#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <ml.hpp>
#include <string.h>
using namespace std;
using namespace cv;
using namespace ml;
/* 验证字母 */
bool verifyLetter(Mat r) {
const float aspect = 45.0f / 77.0f;
float charAspect = (float)r.cols / (float)r.rows;
float error = 0.35;
float minHeight = 15;
float maxHeight = 28;
float minAspect = 0.2;
float maxAspect = aspect + aspect * error;
float area = countNonZero(r);
float bbArea = r.cols * r.rows;
float percPixels = area / bbArea;
return percPixels < 0.8 &&
charAspect > minAspect &&
charAspect < maxAspect &&
r.rows >= minHeight && r.rows <= maxHeight;
}
int main() {
Mat src = imread("2715DTZ.jpg", 0);
Mat copy = imread("2715DTZ.jpg");
equalizeHist(src, src);
imshow("【均衡化后的灰度图】", src);
threshold(src, src, 60, 255, CV_THRESH_BINARY_INV);
imshow("【阈值化后的图像】", src);
//膨胀操作
Mat element = getStructuringElement(0, Size(3, 3));
Mat dst;
dilate(src, dst, element);
imshow("【膨胀后的图像】", dst);
vector<vector<Point>> contours;
findContours(dst, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
for (int i = 0; i < contours.size(); i++) {
drawContours(copy, contours, i, Scalar(0, 255, 0), 1); // 绘制轮廓
Rect rect = boundingRect(contours[i]);
rect.height += 1;
rect.width += 1;
rectangle(copy, rect, Scalar(0,0,255), 1);
Mat roi(src, rect);
if (verifyLetter(roi)) {
// 绘制通过验证的矩形
rectangle(copy, rect, Scalar(255, 0, 0), 1);
// 图像切割
imwrite( to_string(i) + ".jpg", roi);
}
}
imshow("【绘制轮廓】", copy);
waitKey();
return 0;
}
参考:
系列文章
OpenCV自学笔记17. 基于SVM和神经网络的车牌识别(一)
OpenCV自学笔记18. 基于SVM和神经网络的车牌识别(二)
OpenCV自学笔记19. 基于SVM和神经网络的车牌识别(三)
OpenCV自学笔记20. 基于SVM和神经网络的车牌识别(四)