NDK47_OpenCV(一):车牌定位

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)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值