NDK开发汇总
参考开源工程链接
https://www.cnblogs.com/subconscious/p/4022454.html
https://github.com/liuruoze/EasyPR
一 OpenCv识别车牌流程
主要代码
CarRecgnize
#include "CarPlateRecgnize.h"
int main()
{
CarPlateRecgnize p;
Mat src = imread("C:\\Users\\Administrator\\Desktop\\Car\\resource\\test\\test5.jpg");
p.plateRecgnize(src);
waitKey();
return 0;
}
CarPlateRecgnize
#include "CarPlateRecgnize.h"
CarPlateRecgnize::CarPlateRecgnize() {
plateLocation = new CarPlateLocation();
}
CarPlateRecgnize::~CarPlateRecgnize() {
//释放
if (!plateLocation) {
delete plateLocation;
plateLocation = 0;
}
}
/*
* 识别车牌 返回结果给调用者
* 1、定位
* 2、识别
*/
string CarPlateRecgnize::plateRecgnize(Mat src) {
Mat plate;
//定位
plateLocation->location(src,plate);
return string("123");
}
CarPlateLocation
#include "CarPlateLocation.h"
CarPlateLocation::CarPlateLocation() {
}
CarPlateLocation::~CarPlateLocation() {
}
void CarPlateLocation::location(Mat src, Mat& dst) {
//预处理 :去噪 让车牌区域更加突出
Mat blur;
//1、高斯模糊(平滑) (1、为了后续操作 2、降噪 )
GaussianBlur(src,blur,Size(5,5),0);
//imshow("高斯模糊",blur);
Mat gray;
//2、灰度化 去掉颜色 因为它对于我们这里没用 降噪
cvtColor(blur,gray,COLOR_BGR2GRAY);
//imshow("灰度", gray);
Mat sobel_16;
//3、 边缘检测 让车牌更加突出 在调用时需要以16位来保存数据 在后续操作 以及显示的时候需要转回8位
Sobel(gray, sobel_16,CV_16S,1,0);
//转为8位
Mat sobel;
convertScaleAbs(sobel_16,sobel);
//imshow("Sobel", sobel);
//4、二值化 黑白
Mat shold;
//大律法 最大类间算法
threshold(sobel, shold,0,255, THRESH_OTSU + THRESH_BINARY);
//imshow("二值", shold);
//5、闭操作
// 将相邻的白色区域扩大 连接成一个整体
Mat close;
Mat element = getStructuringElement(MORPH_RECT, Size(17, 3));
morphologyEx(shold, close, MORPH_CLOSE, element);
//imshow("闭操作", close);
//6、查找轮廓
//获得初步筛选车牌轮廓================================================================
//轮廓检测
vector< vector<Point> > contours;
//查找轮廓 提取最外层的轮廓 将结果变成点序列放入 集合
findContours(close, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
//遍历
vector<RotatedRect> vec_sobel_roi;
for (vector<Point> point:contours) {
RotatedRect rotatedRect = minAreaRect(point);
//rectangle(src, rotatedRect.boundingRect(), Scalar(255, 0, 255));
//进行初步的筛选 把完全不符合的轮廓给排除掉 ( 比如:1x1,5x1000 )
if (verifySizes(rotatedRect)) {
vec_sobel_roi.push_back(rotatedRect);
}
}
//for (RotatedRect r : vec_sobel_roi) {
//rectangle(src, r.boundingRect(), Scalar(255, 0, 255));
//}
//imshow("q1",src);
//矫正
//因为可能斜的,处理扭曲
//获得候选车牌
vector<Mat> plates;
// 整个图片+经过初步赛选的车牌 + 得到的候选车牌
tortuosity(src, vec_sobel_roi, plates);
//更进一步的筛选
//借助svm 进一步筛选
//imshow("找到轮廓",src);
blur.release();
gray.release();
//......
waitKey();
}
int CarPlateLocation::verifySizes(RotatedRect rotated_rect) {
//容错率
float error = 0.75f;
//训练时候模型的宽高 136 * 32
//获得宽高比
float aspect = float(136) / float(32);
//最小 最大面积 不符合的丢弃
//给个大概就行 随时调整
//尽量给大一些没关系, 这还是初步筛选。
int min = 20 * aspect * 20;
int max = 180 * aspect * 180;
//比例浮动 error认为也满足
//最小宽、高比
float rmin = aspect - aspect * error;
//最大的宽高比
float rmax = aspect + aspect * error;
//矩形的面积
float area = rotated_rect.size.height * rotated_rect.size.width;
//矩形的比例
float r = (float)rotated_rect.size.width / (float)rotated_rect.size.height;
if ((area < min || area > max) || (r < rmin || r > rmax))
return 0;
return 1;
}
/**
* 矫正
*/
void CarPlateLocation::tortuosity(Mat src, vector<RotatedRect> &rects, vector<Mat> &dst_plates) {
//循环要处理的矩形
for (RotatedRect roi_rect : rects) {
float r = (float)roi_rect.size.width / (float)roi_rect.size.height;
//矩形角度
float roi_angle = roi_rect.angle;
//矩形大小
Size roi_rect_size = roi_rect.size;
//让rect在一个安全的范围(不能超过src)
Rect2f rect;
safeRect(src, roi_rect, rect);
//候选车牌
//抠图 这里不是产生一张新图片 而是在src身上定位到一个Mat 让我们处理
//数据和src是同一份
Mat src_rect = src(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 - 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(32, 136, CV_8UC3);
resize(dst, plate_mat, plate_mat.size());
dst_plates.push_back(plate_mat);
dst.release();
}
}
//1、图片 2、要处理的矩形(可能超出有效范围) 3、处理之后在有效范围的矩形
void CarPlateLocation::safeRect(Mat src, RotatedRect rect, Rect2f &dst_rect) {
//转为正常的带坐标的边框
Rect2f boudRect = rect.boundingRect2f();
//左上角 x,y
float tl_x = boudRect.x > 0 ? boudRect.x : 0;
float tl_y = boudRect.y > 0 ? boudRect.y : 0;
//这里是拿 坐标 x,y 从0开始的 所以-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;
dst_rect = Rect2f(tl_x, tl_y, w, h);
}
//1、矫正前 2、矫正后 3、矩形的大小 4、矩形中心点坐标 5、角度
void CarPlateLocation::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),
CV_INTER_CUBIC);
//imshow("旋转前", src);
//imshow("旋转", mat_rotated);
//截取 尽量把车牌多余的区域截取掉
getRectSubPix(mat_rotated, Size(rect_size.width, rect_size.height), center, dst);
//imshow("截取", dst);
mat_rotated.release();
rot_mat.release();
}
二 车牌svm确定与hsv定位
HOG特征
局部归一化的梯度方向直方图,是一种对图像局部重叠区域的密集型描述符, 它通过计算局部区域的梯度方向直方图来构成特征。
参数1(检测窗口)的宽- 参数2(块大小)的宽 结果与参数3(块滑动增量)的余数要为0 高也一样
参数4是胞元大小,参数5是梯度方向
HOGDescriptor hog(Size(128, 64), Size(16, 16), Size(8, 8), Size(8, 8), 3);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n4z6fwhb-1605612123716)(图片/窗口.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UZEOdeRo-1605612123730)(图片/块和步.jpg)]
检测窗口被分为:((128-16)/8+1)*((64-16)/8+1)=105个块(Block);
一个Block有4个胞元(Cell);
一个Cell的Hog描述子向量的长度是9;
统计梯度直方图特征,就是将梯度方向(0-360)划分为x个区间,将图像化为16x16的若干个窗口,每个窗口又划分为x个block,每个block再化为4个cell(8x8)。对每一个cell,算出每一像素点的梯度方向,按梯度方向增加对应bin的值,最终综合N个cell的梯度直方图组成特征。
简单来说,车牌的边缘与内部文字组成的一组信息(在边缘和角点的梯度值是很大的,边缘和角点包含了很多物体的形状信息),HOG就是抽取这些信息组成一个直方图。
HOG : 梯度方向弱化光照的影响,适合捕获轮廓。
LBP : 中心像素的LBP值反映了该像素周围区域的纹理信息。
SVM
http://blog.csdn.net/liukun321/article/details/41574617
简单来说,SVM就是用于区分不同的类型(车牌、非车牌)。SVM的训练数据既有特征又有标签,通过训练,让机器可以自己找到特征和标签之间的联系,在面对只有特征没有标签的数据时,可以判断出标签。属于机器学习中的监督学习。
核函数: 用于将不同类型进行提维
HSV颜色模型
这个模型中颜色的参数分别是:色调(H),饱和度(S),明度(V)。
色调H
用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,品红为300°;
饱和度S
饱和度S表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。
明度V
明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白)。
在OpenCV中hsv 数据为8UC则取值分别为 0-180 0-255 0-255 ,即蓝色应该是120
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Y4p45WL-1605612123733)(图片/hsv.png)]