本文主要是介绍02.OpenCV 车牌识别 sobel定位,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
opencv模块介绍
简介
OpenCV是一个基于BSD许可开源发行的跨平台计算机视觉库。拥有C++,Python和Java接口,并且支持Windows, Linux, Mac OS, iOS 和 Android系统。实现了图像处理和计算机视觉方面的很多通用算法。
模块 | 功能 |
---|---|
Core | 核心基础模块,定义了被所有其他模块和基本数据结构(包括重要的多维数组Mat)使用的基本函数、底层数据结构和算法函数 |
Imgproc | 图像处理模块,包括:滤波、高斯模糊、形态学处理、几何变换、颜色空间转换及直方图计算等 |
Highgui | 高层用户交互模块,包括:GUI、图像与视频I\O等 |
Video | 视频分析,,运动分析及目标跟踪。 |
Calib3d | 3D模块,包括:摄像机标定、立体匹配、3D重建等 |
Features2d | 二维特征检测与描述模块,包括:图像特征检测、描述、匹配等 |
Objdetect | 目标检测模块,如:人脸检测等 |
MI | 机器学习模块,包括:支持向量机、神经网络等 |
Flann | 最近邻开源库。包含一系列查找算法,自动选取最快算法的机制。 |
Imgcodecs | 图像编解码模块,图像文件的读写操作 |
Photo | 图像计算(处理)模块,图像修复及去噪。 |
Shape | 形状匹配算法模块。描述形状、比较形状 |
Stitching | 图像拼接 |
Superres | 超分辨率模块 |
Videoio | 视频读写模块,视频文件包括摄像头的输入。 |
Videostab | 解决拍摄的视频稳定 |
Dnn | 深度神经网络 |
contrib | 可以引入额外模块 |
opencv手册百度网盘:
链接:https://pan.baidu.com/s/15w-bgIWOX8_M5CM2w3VAsQ
提取码:unim
整体流程:
1.高斯模糊
1、高斯模糊:http://www.ruanyifeng.com/blog/2012/11/gaussian_blur.html
目的:对图像去噪,为边缘检测算法做准备。
//1,高斯模糊Mat blur;//ksize: they both must be positive and odd//opencv 只接受奇数半径, 半径越大越模糊GaussianBlur(src, blur, Size(5, 5), 0);imshow("原图", src);//imshow("高斯模糊", blur);
2、灰度化
目的:为边缘检测算法准备灰度化环境。
//2,灰度化Mat gray;cvtColor(blur, gray, COLOR_BGR2GRAY);//imshow("灰度化", gray);
为什么先模糊再灰度化?
如果接收的彩色图片 比灰度化的效果要好。
3、sobel运算(得到图像的一阶水平方向导数)
https://docs.opencv.org/2.4/doc/tutorials/imgproc/imgtrans/sobel_derivatives/sobel_derivatives.html
目的:检测图像中的垂直边缘,便于区分车牌。(Sobel运算只能对灰度图像有效,因此进行sobel运算前必须进行前面的灰度化工作)
//3,Sobel运算Mat sobel_16;//输入图像是8位的, uint8//Sobel函数求导后,导数可能的值会大于255或小于0,Sobel(gray, sobel_16, CV_16S, 1, 0);//imshow("sobel_16", sobel_16);//无法显示//转回8位Mat sobel;convertScaleAbs(sobel_16, sobel);//imshow("sobel", sobel);
4、二值化:(非黑即白)
目的:对图像的每个像素做一个阈值处理。为后续的形态学操作准备。
(灰度图像中,每个像素值是0-255,表示灰暗程度。设定一个阈值t,小于t的设为0,否则设为1)
Mat shold;threshold(sobel, shold, 0, 255, THRESH_OTSU + THRESH_BINARY);
5、形态学操作(闭操作)
目的:将车牌字符连接成一个连通区域,便于取轮廓
形态学操作的对象是二值化图像,腐蚀,膨胀是许多形态学操作的基础。
Mat close;Mat element = getStructuringElement(MORPH_RECT, Size(17, 3));morphologyEx(shold, close, MORPH_CLOSE, element);//imshow("闭操作", close);
腐蚀:
原理:像素x至于模板的中心,根据模版的大小,遍历所有被模板覆盖的其他像素,修改像素x的值为所有像素中最小的值。(对于中心点像素x,模板范围内只要有黑色,就会变成黑色)
膨胀:
原理:与腐蚀操作相反
开操作:
原理:先腐蚀,再膨胀
闭操作:
原理:先膨胀,再腐蚀
6、求轮廓
目的:将连通域的外围画出来,便于形成外接矩形
//6,找轮廓vector<vector<Point>> contours;findContours(close, //输入图像contours, //输出轮廓RETR_EXTERNAL, //外轮廓CHAIN_APPROX_NONE //轮廓上所有像素点);RotatedRect rotatedRect;vector<RotatedRect> vec_sobel_rects;
7、尺寸判断
目的:初步筛选排除不可能是车牌的矩形(中国车牌的一般大小是440mm*140mm,宽高比为3.14)
//7,遍历并判断矩形尺寸for each (vector<Point> points in contours){//最小外接矩形rotatedRect = minAreaRect(points);//带角度的矩形//rectangle(src, rotatedRect.boundingRect(), Scalar(0, 0, 255));//尺寸校验if (verifySizes(rotatedRect)) {vec_sobel_rects.push_back(rotatedRect);}}for each (RotatedRect rect in vec_sobel_rects){//rectangle(src, rect.boundingRect(), Scalar(0, 255, 0));}//imshow("找轮廓", src);
/**
* 尺寸校验(宽高比&面积)
*/
int PlateLocate::verifySizes(RotatedRect rotatedRect)
{//容错率float error = 0.75f;//理想宽高比float aspect = float(136) / float(36);//真实宽高比float realAspect = float(rotatedRect.size.width) / float(rotatedRect.size.height);if (realAspect < 1) realAspect = (float)rotatedRect.size.height / (float)rotatedRect.size.width;//真实面积float area = rotatedRect.size.height * rotatedRect.size.width;//最小 最大面积 不符合的丢弃//给个大概就行 随时调整//尽量给大一些没关系, 这还是初步筛选。int areaMin = 44 * aspect * 14;int areaMax = 440 * aspect * 140;//比例浮动 error认为也满足//最小宽高比float aspectMin = aspect - aspect * error;//最大宽高比float aspectMax = aspect + aspect * error;if ((area < areaMin || area > areaMax) || (realAspect < aspectMin || realAspect > aspectMax))return 0;return 1;
}
8、角度判断
目的:初步筛选排除不可能是车牌的矩形
9、旋转矩形
目的:将偏斜的车牌调整为水平,为后面的车牌判断与字符识别提高成功率
仿射变换
10、调整大小
目的:确保候选车牌导入机器学习模型之前尺寸一致
// 矩形矫正(角度判断,旋转矩形,调整大小)tortuosity(src, vec_sobel_rects, dst_plates);for each (Mat m in dst_plates){imshow("sobel定位候选车牌", m);waitKey();}
/**
* 矩形矫正
*/
void PlateLocate::tortuosity(Mat src, vector<RotatedRect>& rects, vector<Mat>& dst_plates)
{//循环要处理的矩形for (RotatedRect roi_rect : rects) {//矩形角度float roi_angle = roi_rect.angle;float r = (float)roi_rect.size.width / (float)roi_rect.size.height;if (r < 1) {roi_angle = 90 + roi_angle;}//矩形大小Size roi_rect_size = roi_rect.size;//让rect在一个安全的范围(不能超过src)Rect2f safa_rect;safeRect(src, roi_rect, safa_rect);//候选车牌//抠图 这里不是产生一张新图片 而是在src身上定位到一个Mat 让我们处理//数据和src是同一份Mat src_rect = src(safa_rect);//真正的候选车牌Mat dst;//不需要旋转的 旋转角度小没必要旋转了if (roi_angle - 5 < 0 && roi_angle + 5 > 0) {dst = src_rect.clone();}else {//相对于roi的中心点 不减去左上角坐标是相对于整个图的//减去左上角则是相对于候选车牌的中心点 坐标Point2f roi_ref_center = roi_rect.center - safa_rect.tl();Mat rotated_mat;//矫正 rotated_mat: 矫正后的图片rotation(src_rect, rotated_mat, roi_rect_size, roi_ref_center, roi_angle);dst = rotated_mat;}//调整大小Mat plate_mat;//高+宽plate_mat.create(36, 136, CV_8UC3);resize(dst, plate_mat, plate_mat.size());dst_plates.push_back(plate_mat);dst.release();}
}
/**
* 转换安全矩形
*/
void PlateLocate::safeRect(Mat src, RotatedRect rect, Rect2f& safa_rect)
{//RotatedRect 没有坐标//转为正常的带坐标的边框Rect2f boudRect = rect.boundingRect2f();//左上角 x,yfloat tl_x = boudRect.x > 0 ? boudRect.x : 0;float tl_y = boudRect.y > 0 ? boudRect.y : 0;//这里是拿 坐标 x,y 从0开始的 所以-1//比如宽长度是10,x坐标最大是9, 所以src.clos-1 //右下角float br_x = boudRect.x + boudRect.width < src.cols? boudRect.x + boudRect.width - 1: src.cols - 1;float br_y = boudRect.y + boudRect.height < src.rows? boudRect.y + boudRect.height - 1: src.rows - 1;float w = br_x - tl_x;float h = br_y - tl_y;if (w <= 0 || h <= 0) return;safa_rect = Rect2f(tl_x, tl_y, w, h);
}
/**
* 旋转
*/
void PlateLocate::rotation(Mat src, Mat& dst, Size rect_size, Point2f center, double angle)
{//获得旋转矩阵Mat rot_mat = getRotationMatrix2D(center, angle, 1);//运用仿射变换Mat mat_rotated;//矫正后 大小会不一样,但是对角线肯定能容纳int max = sqrt(pow(src.rows, 2) + pow(src.cols, 2));warpAffine(src, mat_rotated, rot_mat, Size(max, max),INTER_CUBIC);//imshow("旋转前", src);//imshow("旋转后", mat_rotated);//截取 尽量把车牌多余的区域截取掉getRectSubPix(mat_rotated, Size(rect_size.width, rect_size.height), center, dst);//imshow("截取后", dst);//waitKey();mat_rotated.release();rot_mat.release();
}
这篇关于02.OpenCV 车牌识别 sobel定位的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!