opencv实现车牌提取

使用边缘检测-形态学-提取矩形区域得到最终的车牌

代码和详细解释如下:

#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
//提取竖直的sobel边缘
void SobelVerEdge(Mat& src, Mat& result)
{
	CV_Assert(src.channels()==1);
	src.convertTo(src,CV_32FC1);
	//水平方向的sobel算子,要用卷积就要使得模板旋转180度
	Mat sobelx = (Mat_(3,3)<<-0.125,0,0.125,-0.25,0,0.25,-0.125,0,0.125);
	Mat conResMat;
	filter2D(src,conResMat,src.type(),sobelx);
	//计算梯度幅值
	Mat graMagMat;
	multiply(conResMat,conResMat,graMagMat);
	cout << graMagMat.size() << endl;
	//根据梯度幅值和参数设置阈值
	int scaleVal=4;
	//mean函数得到Mat中各个通道的均值,因为该Mat矩阵只有一个通道,所以提取0就可以
	double thresh = scaleVal*mean(graMagMat).val[0];
	Mat resultTemp = Mat::zeros(graMagMat.size(),graMagMat.type());
	float* pDataMag = (float*)graMagMat.data;
	float* pDataRes = (float*)resultTemp.data;
	int nRows = graMagMat.rows;
	int nCols = graMagMat.cols;
	for (int i = 1; i < nRows - 1; i++)
	{
		for (int j = 1; j < nCols - 1; j++)
		{
			bool b1 = (pDataMag[i*nCols+j]>pDataMag[i*nCols + j-1]);
			bool b2 = (pDataMag[i*nCols + j]>pDataMag[i*nCols + j + 1]);
			bool b3 = (pDataMag[i*nCols + j]>pDataMag[(i-1)*nCols + j]);
			bool b4 = (pDataMag[i*nCols + j]>pDataMag[(i + 1)*nCols + j]);
			pDataRes[i*nCols + j] = 255 * ((pDataMag[i*nCols + j] > thresh) && ((b1&&b2) || (b3&&b4)));
		}
	}
	resultTemp.convertTo(resultTemp,CV_8UC1);
	result = resultTemp.clone();
}

//疑似区域提取
Mat getPlateArea(Mat& src, Mat SobelMat)
{
	//由图可知,车牌部分为蓝色,所以可将其转换到HSV空间,然后提取蓝色通道部分
	Mat img_h, img_s, img_v, imghsv;
	vector hsv_vec;
	cvtColor(src,imghsv,CV_BGR2HSV);
	imshow("hsv",imghsv);
	waitKey(0);
	//分割hsv通道
	split(imghsv,hsv_vec);
	img_h = hsv_vec[0];
	img_s = hsv_vec[1];
	img_v = hsv_vec[2];
	img_h.convertTo(img_h,CV_32F);
	img_s.convertTo(img_s, CV_32F);
	img_v.convertTo(img_v, CV_32F);
	double max_s, max_h, max_v;
//取最大值,第一个参数是Mat矩阵,第二个:double* minValue,第三个:double* maxValue, int minIdx,int maxIdx
	minMaxIdx(img_h,0,&max_h);
	minMaxIdx(img_s, 0, &max_s);
	minMaxIdx(img_v, 0, &max_v);
	//各通道归一化
	img_h = img_h / max_h;
	img_s = img_s / max_s;
	img_v = img_v / max_v;
	//取蓝色通道元素
	Mat bw_blue = ((img_h > 0.45) & (img_h<0.75) &(img_s>0.15) &(img_v > 0.25));
	int height = bw_blue.rows;
	int width = bw_blue.cols;
	Mat bw_blue_edge = Mat::zeros(bw_blue.size(),bw_blue.type());
	imshow("bw_blue",bw_blue);
	waitKey(0);
	//车牌疑似区域提取,既要是边缘点,也要是蓝色通道像素
	for (int k = 1; k < height - 1; k++)
	{
		for (int l = 1; l < width - 1; l++)
		{
			Rect rct;
			rct.x = l - 1;
			rct.y = k - 1;
			rct.height = 3;
			rct.width = 3;
			if ((SobelMat.at(k, l) == 255) && (countNonZero(bw_blue(rct)>1)))
				bw_blue_edge.at(k, l) = 255;
		}
	}
	//形态学操作
	Mat morph;
	morphologyEx(bw_blue_edge,morph,MORPH_CLOSE,Mat::ones(2,25,CV_8UC1));
	imshow("morph",morph);
	vector> region_contours;
	findContours(morph.clone(),region_contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE,Point(0,0));
	Mat result;
	for (int n = 0; n < region_contours.size(); n++)
	{
		//计算最小外接矩形内非0点的个数所占的比例,以及宽高比
		Rect rect = boundingRect(region_contours[n]);//给轮廓画一个最小外接矩形
		int sub = countNonZero(morph(rect));//计算最小外接矩形中非0点的个数
		double ratio = double(sub) / rect.area();//计算最小外接矩形中非零点占最小外接矩形总点数的比例
		double wh_ratio = double(rect.width) / rect.height;
		if (ratio > 0.5 && wh_ratio > 2 && wh_ratio < 5 && rect.height>12 && rect.width > 60)
		{
			result = src(rect);
			imshow("Rect",result);
			waitKey(0);
		}

	}
	return result;
}

int main()
{
	//imread中0表示灰度返回,1表示原图返回
	Mat srcImage = imread("E:\\研究生\\学习材料\\学习书籍\\OpenCV图像处理编程实例-源码-20160801\\《OpenCV图像处理编程实例-源码-20160801\\images\\car.jpg");
	if (!srcImage.data)
		return -1;
	Mat srcGray;
	cvtColor(srcImage,srcGray,CV_BGR2GRAY);
	Mat sobelMat;
	SobelVerEdge(srcGray, sobelMat);
	Mat result = getPlateArea(srcImage,sobelMat);
	return 0;
}

 

你可能感兴趣的:(opencv学习)