【OpenCV3.4.7+VS2017】基于颜色阈值分割以及对于蓝色车的边缘检测(川字分割)(模板匹配)的车牌识别项目(分版块代码可直接运行)

一、车牌的识别和校正

本文采用一工程多项目模式,以代码呈现,因还未接触MFC,所以敬请见谅,之后会继续学习,不断完善代码。

车牌识别借鉴于CSDN博主吾理小子的博客,表达由衷的感谢!https://blog.csdn.net/qq_39960119/article/details/83930112
对其中的一些参数和定义做了一些修改,增加了对倾斜图片的修正,不过鄙人对于倾斜角度参数的理解依旧不到位,因此对于角度的处理还是不太理解,属实惭愧。

#include <iostream>
#include <opencv2\opencv.hpp>

using namespace std;
using namespace cv;

int main()

{
    
    

	Mat OriginalImg;
	
	OriginalImg = imread("TestPhoto.jpg", IMREAD_COLOR);//读取原始彩色图像
	
	if (OriginalImg.empty())  //判断图像对否读取成功

	{
    
    

		cout << "错误!读取图像失败\n";

		return -1;

	}

	//	imshow("原图", OriginalImg); //显示原始图像

	cout << "Width:" << OriginalImg.rows << "\tHeight:" << OriginalImg.cols << endl;//打印长宽



	Mat ResizeImg;

	//if (OriginalImg.cols > 640)

	resize(OriginalImg, ResizeImg, Size(640, 640 * OriginalImg.rows / OriginalImg.cols));

	imshow("尺寸变换图", ResizeImg);



	unsigned char pixelB, pixelG, pixelR;  //记录各通道值

	unsigned char DifMax = 65;             //基于颜色区分的阈值设置

	unsigned char B = 200, G = 80, R = 50; //各通道的阈值设定,针对与蓝色车牌

	Mat BinRGBImg = ResizeImg.clone();  //二值化之后的图像

	int i = 0, j = 0;

	for (i = 0; i < ResizeImg.rows; i++)   //通过颜色分量将图片进行二值化处理

	{
    
    

		for (j = 0; j < ResizeImg.cols; j++)

		{
    
    

			pixelB = ResizeImg.at<Vec3b>(i, j)[0]; //获取图片各个通道的值

			pixelG = ResizeImg.at<Vec3b>(i, j)[1];

			pixelR = ResizeImg.at<Vec3b>(i, j)[2];



			if (abs(pixelB - B) < DifMax && abs(pixelG - G) < DifMax && abs(pixelR - R) < DifMax)

			{
    
                                               //将各个通道的值和各个通道阈值进行比较

				BinRGBImg.at<Vec3b>(i, j)[0] = 255;     //符合颜色阈值范围内的设置成白色

				BinRGBImg.at<Vec3b>(i, j)[1] = 255;

				BinRGBImg.at<Vec3b>(i, j)[2] = 255;

			}

			else

			{
    
    

				BinRGBImg.at<Vec3b>(i, j)[0] = 0;        //不符合颜色阈值范围内的设置为黑色

				BinRGBImg.at<Vec3b>(i, j)[1] = 0;

				BinRGBImg.at<Vec3b>(i, j)[2] = 0;

			}

		}

	}

	imshow("基于颜色信息二值化", BinRGBImg);        //显示二值化处理之后的图像



	Mat BinOriImg;     //形态学处理结果图像

	Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); //设置形态学处理窗的大小
    dilate(BinRGBImg, BinOriImg, element,Point(-1, -1), 5);     //进行多次膨胀操作
	erode(BinOriImg, BinOriImg, element,Point(-1,-1),5);      //进行多次腐蚀操作
	imshow("形态学处理后", BinOriImg);        //显示形态学处理之后的图像
	
	//--------------------------------------------------------------------------


	double length, area, rectArea;     //定义轮廓周长、面积、外界矩形面积

	double rectDegree = 0.0;           //矩形度=外界矩形面积/轮廓面积,比值越大说明效果越好

	double long2Short = 0.0;           //体态比=长边/短边

	CvRect rect;           //外界矩形:结构体包含x,y坐标,width和height

	CvBox2D box, boxTemp;  //外接矩形

	CvPoint2D32f pt[4];    //矩形定点变量

	double axisLong = 0.0, axisShort = 0.0;        //矩形的长边和短边

	/*double axisLongTemp = 0.0, axisShortTemp = 0.0;*///矩形的长边和短边

	double LengthTemp;     //中间变量

	float  angle = 0;      //记录车牌的倾斜角度

	bool   TestPlantFlag = 0;  //车牌检测成功标志位

	cvtColor(BinOriImg, BinOriImg, CV_BGR2GRAY);   //将形态学处理之后的图像转化为灰度图像

	threshold(BinOriImg, BinOriImg, 100, 255, THRESH_BINARY); //灰度图像二值化,//OTSU算法(双峰图效果明显)

	CvMemStorage *storage = cvCreateMemStorage(0);//跟栈类似

	CvSeq * seq = 0;     //创建一个序列,CvSeq本身就是一个可以增长的序列,不是固定的序列

	CvSeq * tempSeq = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);//以点坐标形式,序列头大小,储存元素大小,储存在之前的容器里

	int cnt = cvFindContours(&(IplImage(BinOriImg)), storage, &seq, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

	//第一个参数是IplImage指针类型,将MAT强制转换为IplImage指针类型

	//返回轮廓的数目 

	//获取二值图像中轮廓的个数

	cout << "number of contours   " << cnt << endl;  //打印轮廓个数

	for (tempSeq = seq; tempSeq != NULL; tempSeq = tempSeq->h_next)

	{
    
    

		length = cvArcLength(tempSeq);       //获取轮廓周长

		area = cvContourArea(tempSeq);       //获取轮廓面积

		if (area > 800 && area < 50000)     //矩形区域面积大小判断

		{
    
    

			rect = cvBoundingRect(tempSeq, 1);//计算矩形边界

			boxTemp = cvMinAreaRect2(tempSeq, 0);  //获取轮廓的矩形

			cvBoxPoints(boxTemp, pt);              //获取矩形四个顶点坐标

			angle = boxTemp.angle;                 //得到车牌倾斜角度



			axisLong = sqrt(pow(pt[1].x - pt[0].x, 2) + pow(pt[1].y - pt[0].y, 2));  //计算长轴(勾股定理)

			axisShort = sqrt(pow(pt[2].x - pt[1].x, 2) + pow(pt[2].y - pt[1].y, 2)); //计算短轴(勾股定理)
			Point2d points;
			vector<Point>dots;
			for (int i = 0; i < 4; i++)
			{
    
    
				points.x = pt[i].x;
				points.y = pt[i].y;
				dots.push_back(points);
			}
			RotatedRect rect = minAreaRect(dots);

			if (axisShort > axisLong)   //短轴大于长轴,交换数据

			{
    
    

				LengthTemp = axisLong;

				axisLong = axisShort;

				axisShort = LengthTemp;

			}

			else

				angle += 90;


			rectArea = axisLong * axisShort;  //计算矩形的面积

			rectDegree = area / rectArea;     //计算矩形度(比值越接近1说明越接近矩形)



			long2Short = axisLong / axisShort; //计算长宽比

			if (long2Short > 2.2 && long2Short < 3.8 && rectDegree > 0.63 && rectDegree < 1.37 && rectArea > 2000 && rectArea < 50000)

			{
    
    

				Mat GuiRGBImg = ResizeImg.clone();

				TestPlantFlag = true;             //检测车牌区域成功

				for (int i = 0; i < 4; ++i)       //划线框出车牌区域

					cvLine(&(IplImage(GuiRGBImg)), cvPointFrom32f(pt[i]), cvPointFrom32f(pt[((i + 1) % 4) ? (i + 1) : 0]), CV_RGB(255, 0, 0));//实现闭口画线

				imshow("提取车牌结果图", GuiRGBImg);    //显示最终结果图

			

				if (angle != 0)
				{
    
    
					Point2f center(pt->x + (axisLong / 2), pt->y - (axisLong / 2));
					Mat warp = getRotationMatrix2D(center, angle, 1.0);
					warpAffine(OriginalImg, OriginalImg, warp, Size(640, 640 * OriginalImg.rows / OriginalImg.cols));//不设置会出现内存问题
					resize(OriginalImg, OriginalImg, Size(640, 640 * OriginalImg.rows / OriginalImg.cols));
					imshow("旋转后的原图", OriginalImg);
					imwrite("affineimg.jpg", OriginalImg);
				}
				else
				{
    
    
					Mat img_ROI = GuiRGBImg(Rect(pt->x+2, pt->y - axisShort+2, axisLong, axisShort));//提取感兴趣区域这里+2是为了修正红色矩形边框
					imshow("车牌", img_ROI);
					resize(img_ROI, img_ROI, Size(354, 118));
					imshow("车牌2", img_ROI);
					imwrite("affineimg.jpg", img_ROI);
				}
				cout << "倾斜角度:" << angle << endl;

			}

		}

	}
	waitKey();

	return 0;



}

代码运行效果图如下:

运行结果

二、获取车牌

此处是对修正后的原图进行的处理,提取为354×118像素的车牌校正后的图片
识别与提取的代码上面写过了,拿过来用就行。

Mat img_ROI = GuiRGBImg(Rect(pt->x - axisLongTemp+2, pt->y - axisShortTemp+2, axisLongTemp-5, axisShortTemp-5));//提取感兴趣区域

				imshow("车牌", img_ROI);
				resize(img_ROI, img_ROI, Size(354, 118));
				imshow("车牌2", img_ROI);
				imwrite("img_ROI.jpg", img_ROI);

运行结果如下:运行结果

二·1 边缘检测法

借鉴于CSDN博主Nine-days的部分代码,并做了一些完善和普适兼容。表达由衷的感谢!https://blog.csdn.net/u011808673/article/details/78510692

int main()
{
    
    
	Mat OriginalImg;

	OriginalImg = imread("blurcar.jpg", IMREAD_COLOR);//读取原始彩色图像

	if (OriginalImg.empty())  //判断图像对否读取成功

	{
    
    

		cout << "错误!读取图像失败\n";

		return -1;

	}
	cout << "Width:" << OriginalImg.rows << "\tHeight:" << OriginalImg.cols << endl;//打印长宽



	Mat ResizeImg;
	
	resize(OriginalImg, ResizeImg, Size(640, 640 * OriginalImg.rows / OriginalImg.cols));

	imshow("尺寸变换图", ResizeImg);
	Mat gray_img;
	cvtColor(ResizeImg, gray_img, CV_RGB2GRAY);
	Mat blur_img;
	blur(gray_img, blur_img, Size(3, 3));
	Mat candy_img;
	Canny(blur_img, candy_img, 300, 100, 3);
	imshow("test", candy_img);

	//形态学处理

    //图片膨胀处理

	Mat dilate_image, erode_image, BinOriImg;

	//自定义 核进行 x 方向的膨胀腐蚀

	Mat elementX = getStructuringElement(MORPH_RECT, Size(22, 1));

	Mat elementY = getStructuringElement(MORPH_RECT, Size(1, 20));

	Point point(-1, -1);

	dilate(candy_img, dilate_image, elementX, point, 2);

	erode(dilate_image, erode_image, elementX, point, 4);

	dilate(erode_image, dilate_image, elementX, point, 2);



	//自定义 核进行 Y 方向的膨胀腐蚀

	erode(dilate_image, erode_image, elementY, point, 1);

	dilate(erode_image, BinOriImg, elementY, point, 2);

	imwrite("dilate_image.jpg", BinOriImg);

	//噪声处理

    //平滑处理 中值滤波

	Mat blur_image;

	medianBlur(BinOriImg, blur_image, 15);

	medianBlur(blur_image, blur_image, 15);

	imshow("test2", blur_image);

接下来就是对处理过的图像进行车牌提取,代码上面写过了,拿来用就行,运行结果如下图:运行结果

三、字符分割

此字符分割解决了垂直投影切割“川”字和其他易于被分割错误的汉字的问题,简化了对于车牌中的点被分割的问题。
借鉴于博主lxx_123456的文章,表达由衷的感谢!https://blog.csdn.net/lxx_123456/article/details/79078570

#define _CRT_SECURE_NO_WARNINGS
#define cols_value 0    //cols:2   row:1对于edge搜索
#define row_value 0      //对于川来说不需要去边框
#include <opencv2/opencv.hpp>
#include <math.h>
#include<vector>
#include<cv.h>
using namespace cv;
using namespace std;

vector<Mat> verticalProjectionMat(Mat Image)//封装垂直投影  
{
    
    
	int perPixelValue;//每个像素的值  
	int width = Image.cols;
	int height = Image.rows;
	printf("图片的宽%d图片的高%d", width, height);
	int* projectValArry = new int[width];//创建用于储存每列白色像素个数的数组  
	memset(projectValArry, 0, width * 4);//初始化数组  
	for (int col = 0; col < width; col++)//列  
	{
    
    
		int cols_convert_num = 0;
		for (int i = 0; i < height - 1; i++)
		{
    
    
			if (Image.at<uchar>(i, col) != Image.at<uchar>(i + 1, col))
				cols_convert_num++;
		}
		if (cols_convert_num < cols_value)
		{
    
    
			continue;
		}
		for (int row = 0; row < height; row++)//行  
		{
    
    
			int row_convert_num = 0;
			for (int j = 0; j < width - 1; j++)
			{
    
    
				if (Image.at<uchar>(row,j) != Image.at<uchar>(row,j+1))
					row_convert_num++;
			}
			if (row_convert_num < row_value)
			{
    
    
				continue;
			}
			perPixelValue = Image.at<uchar>(row, col);//每个像素的值
			//if (perPixelValue == 0)//如果是白底黑字  
			if (perPixelValue == 255)//如果是黑底白字
			{
    
    
				projectValArry[col]++;//列上的叠加
			}
		}
	}
	Mat verticalProjectionMat(height, width, CV_8U, Scalar(255));//垂直投影的画布  
	for (int i = 0; i < height; i++)
	{
    
    
		for (int j = 0; j < width; j++)
		{
    
    
			perPixelValue = 255;  //背景设置为白色  
			verticalProjectionMat.at<uchar>(i, j) = perPixelValue;//遍历设置背景颜色
		}
	}
	for (int i = 0; i < width; i++)//垂直投影直方图  
	{
    
    
		for (int j = 0; j < projectValArry[i]; j++)
		{
    
    
			perPixelValue = 0;  //直方图设置为黑色    
			verticalProjectionMat.at<uchar>(height - 1 - j, i) = perPixelValue;
		}
	}

	imshow("垂直投影", verticalProjectionMat);//以上是如何让这个画布形成的呢
	Rect rect(0, 0, 120, 40);
	Mat image_cut = Mat(verticalProjectionMat, rect);
	Mat image_copy = image_cut.clone();
	//imshow("切割图片", image_copy);

	vector<Mat> roiList;//用于储存分割出来的每个字符  
	int startIndex = 0;//记录进入字符区的索引  
	int endIndex = 0;//记录进入空白区域的索引  
	bool inBlock = false;//是否遍历到了字符区内  
	for (int i = 0; i < Image.cols; i++)//cols=width  
	{
    
    
		if (!inBlock && projectValArry[i] != 0)//进入字符区  
		{
    
    
			inBlock = true;
			startIndex = i;
		}
		else if (projectValArry[i] ==0 && inBlock)//进入空白区  
		{
    
    
			
			while (i < Image.cols / 7)//分割汉字
			{
    
    
				i++;
			}
			
			endIndex = i;
			inBlock = false;			
			Mat roiImg = Image(Range(0, Image.rows), Range(startIndex, endIndex + 1));
			roiList.push_back(roiImg);
		}
	}
	delete[] projectValArry;
	return roiList;
}
int main()
{
    
    
	Point point(-1, -1);

	//Mat Image = imread("E:\\LicenseRecognition\\EdgeSearch\\EdgeSearch\\img_ROI.jpg");
    Mat Image = imread("E:\\LicenseRecognition\\EdgeSearch\\GetPointedLabel\\img_ROI.jpg");//可用不同的路径
	Mat Image1;
	cvtColor(Image, Image1, CV_BGR2GRAY);
	imshow("灰度化", Image1);
	Mat Image2;
	threshold(Image1, Image2, 158, 255, CV_THRESH_BINARY);//二值化//100,255 Edge//157,255颜色分割 162edge分割
	imshow("二值化", Image2);
	Mat Image3;
	Mat element = getStructuringElement(MORPH_RECT, Size(1,1));
	morphologyEx(Image2, Image3, MORPH_OPEN, element,point,4);//开运算	
	imshow("开运算", Image3);
	int size = 0;
	char szName[30] = {
    
     0 };
	vector<Mat> b = verticalProjectionMat(Image3);
	for (int j = 0; j < b.size(); j++)
	{
    
    
		if (j == 2)//去除车牌中的点
		{
    
    
			continue;
		}		
		sprintf(szName, "vertical_%d.jpg", j);
		resize(b[j], b[j],Size(20, 40));//不可调整顺序,不然质量差
		imshow(szName, b[j]);
		imwrite(szName, b[j]);

	}
	waitKey(0);
}

分割效果:分割效果

四、模板匹配以及识别

借鉴于某车牌识别系统开源源码中模板匹配及识别一小部分。
缺点1:8与B、5与6的识别不够准确(因为是像素相减),幸好鄙人多测试了几张图,发现8与B、5与6的判断标准恰巧是相反的,算作投机取巧,此为学习研究人士的大忌,若无奈之才疏学浅,实不可取。
缺点2:识别的汉字有限,代码后加了其他省份的车牌汉字可用作代码识别修改
如有大佬有解决缺点完善代码之法可以积极评论指正,在下感激不尽!

//此为head.h头文件
#pragma once
#include "cv.h"
#include "highgui.h"

struct pattern
{
    
    

	double feature[33]; //样本的特征向量
	int number;        //待识别字符在样本库中的序列号

};

//定义特征提取函数
void GetFeature(IplImage *src, pattern &pat);
#define _CRT_SECURE_NO_WARNINGS
#include "head.h"
#include<opencv2/opencv.hpp>
#include<cstring>
#include<highgui/highgui.hpp>
using namespace cv;
using namespace std;
void GetFeature(IplImage* src, pattern &pat)
{
    
    
	CvScalar s;
	int i, j;
	for (i = 0; i < 33; i++)
		pat.feature[i] = 0.0;
	//图像大小是20*40大小的,分成25块

	//********第一行***********	
	//第一块
	for (j = 0; j < 8; j++)
	{
    
    
		for (i = 0; i < 4; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[0] += 1.0;
		}
	}

	//第二块
	for (j = 0; j < 8; j++)
	{
    
    
		for (i = 4; i < 8; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[1] += 1.0;
			
			
		}
	}
	//第三块
	for (j = 0; j < 8; j++)
	{
    
    
		for (i = 8; i < 12; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[2] += 1.0;
		}
	}
	//第四块
	for (j = 0; j < 8; j++)
	{
    
    
		for (i = 12; i < 16; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[3] += 1.0;
		}
	}
	//第五块
	for (j = 0; j < 8; j++)
	{
    
    
		for (i = 16; i < 20; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[4] += 1.0;
		}
	}
	//********第二行***********	
	//第六块
	for (j = 8; j < 16; j++)
	{
    
    
		for (i = 0; i < 4; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[5] += 1.0;
		}
	}
	//第七块
	for (j = 8; j < 16; j++)
	{
    
    
		for (i = 4; i < 8; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[6] += 1.0;
		}
	}
	//第八块
	for (j = 8; j < 16; j++)
	{
    
    
		for (i = 8; i < 12; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[7] += 1.0;
		}
	}
	//第九块
	for (j = 8; j < 16; j++)
	{
    
    
		for (i = 12; i < 16; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[8] += 1.0;
		}
	}
	//第十块
	for (j = 8; j < 16; j++)
	{
    
    
		for (i = 16; i < 20; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[9] += 1.0;
		}
	}
	//********第三行***********
	//第十一块
	for (j = 16; j < 24; j++)
	{
    
    
		for (i = 0; i < 4; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[10] += 1.0;
		}
	}
	//第十二块
	for (j = 16; j < 24; j++)
	{
    
    
		for (i = 4; i < 8; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[11] += 1.0;
		}
	}
	//第十三块
	for (j = 16; j < 24; j++)
	{
    
    
		for (i = 8; i < 12; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[12] += 1.0;
		}
	}
	//第十四块
	for (j = 16; j < 24; j++)
	{
    
    
		for (i = 12; i < 16; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[13] += 1.0;
		}
	}
	//第十五块
	for (j = 16; j < 24; j++)
	{
    
    
		for (i = 16; i < 20; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[14] += 1.0;
		}
	}
	//********第四行***********
	//第十六块
	for (j = 24; j < 32; j++)
	{
    
    
		for (i = 0; i < 4; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[15] += 1.0;
		}
	}
	//第十七块
	for (j = 24; j < 32; j++)
	{
    
    
		for (i = 4; i < 8; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[16] += 1.0;
		}
	}
	//第十八块
	for (j = 24; j < 32; j++)
	{
    
    
		for (i = 8; i < 12; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[17] += 1.0;
		}
	}
	//第十九块
	for (j = 24; j < 32; j++)
	{
    
    
		for (i = 12; i < 16; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[18] += 1.0;
		}
	}
	//第二十块
	for (j = 24; j < 32; j++)
	{
    
    
		for (i = 16; i < 20; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[19] += 1.0;
		}
	}
	//********第五行***********
	//第二十一块
	for (j = 32; j < 40; j++)
	{
    
    
		for (i = 0; i < 4; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[20] += 1.0;
		}
	}
	//第二十二块
	for (j = 32; j < 40; j++)
	{
    
    
		for (i = 4; i < 8; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[21] += 1.0;
		}
	}
	//第二十三块
	for (j = 32; j < 40; j++)
	{
    
    
		for (i = 8; i < 12; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[22] += 1.0;
		}
	}
	//第二十四块
	for (j = 32; j < 40; j++)
	{
    
    
		for (i = 12; i < 16; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[23] += 1.0;
		}
	}
	//第二十五块
	for (j = 32; j < 40; j++)
	{
    
    
		for (i = 16; i < 20; i++)
		{
    
    
			s = cvGet2D(src, j, i);
			if (s.val[0] == 255)
				pat.feature[24] += 1.0;
		}
	}

	//下面统计方向交点特征
	for (i = 0; i < 20; i++)
	{
    
    
		s = cvGet2D(src, 8, i);
		if (s.val[0] == 255)
			pat.feature[25] += 1.0;
	}
	for (i = 0; i < 20; i++)
	{
    
    
		s = cvGet2D(src, 16, i);
		if (s.val[0] == 255)
			pat.feature[26] += 1.0;
	}
	for (i = 0; i < 20; i++)
	{
    
    
		s = cvGet2D(src, 24, i);
		if (s.val[0] == 255)
			pat.feature[27] += 1.0;
	}
	for (i = 0; i < 20; i++)
	{
    
    
		s = cvGet2D(src, 32, i);
		if (s.val[0] == 255)
			pat.feature[28] += 1.0;
	}
	for (j = 0; j < 40; j++)
	{
    
    
		s = cvGet2D(src, j, 4);
		if (s.val[0] == 255)
			pat.feature[29] += 1.0;
	}
	for (j = 0; j < 40; j++)
	{
    
    
		s = cvGet2D(src, j, 8);
		if (s.val[0] == 255)
			pat.feature[30] += 1.0;
	}
	for (j = 0; j < 40; j++)
	{
    
    
		s = cvGet2D(src, j, 12);
		if (s.val[0] == 255)
			pat.feature[31] += 1.0;
	}
	for (j = 0; j < 40; j++)
	{
    
    
		s = cvGet2D(src, j, 16);
		if (s.val[0] == 255)
			pat.feature[32] += 1.0;
	}
}


int main()
{
    
    

	IplImage * dst_image[7];
	
		dst_image[0]=  cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterSeperate\\vertical_0.jpg", 0);
		dst_image[1] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterSeperate\\vertical_1.jpg", 0);
		dst_image[2] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterSeperate\\vertical_3.jpg", 0);
		dst_image[3] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterSeperate\\vertical_4.jpg", 0);
		dst_image[4] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterSeperate\\vertical_5.jpg", 0);
		dst_image[5] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterSeperate\\vertical_6.jpg", 0);
		dst_image[6] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterSeperate\\vertical_7.jpg", 0);

	IplImage * char_sample[34];//字符样本图像数组
	IplImage * hanzi_sample[9];//汉字样本图像数组
	pattern char_pattern[34];//定义字符样品库结构数组
	pattern hanzi_pattern[9];//定义汉字样品库结构数组
	pattern TestSample[7];//定义待识别字符结构数组

	//载入字符模板
	char_sample[0] = cvLoadImage("template\\0.bmp", 0);
	char_sample[1] = cvLoadImage("template\\1.bmp", 0);
	char_sample[2] = cvLoadImage("template\\2.bmp", 0);
	char_sample[3] = cvLoadImage("template\\3.bmp", 0);
	char_sample[4] = cvLoadImage("template\\4.bmp", 0);
	char_sample[5] = cvLoadImage("template\\5.bmp", 0);
	char_sample[6] = cvLoadImage("template\\6.bmp", 0);
	char_sample[7] = cvLoadImage("template\\7.bmp", 0);
	char_sample[8] = cvLoadImage("template\\8.bmp", 0);
	char_sample[9] = cvLoadImage("template\\9.bmp", 0);
	char_sample[10] = cvLoadImage("template\\A.bmp", 0);
	char_sample[11] = cvLoadImage("template\\B.bmp", 0);
	char_sample[12] = cvLoadImage("template\\C.bmp", 0);
	char_sample[13] = cvLoadImage("template\\D.bmp", 0);
	char_sample[14] = cvLoadImage("template\\E.bmp", 0);
	char_sample[15] = cvLoadImage("template\\F.bmp", 0);
	char_sample[16] = cvLoadImage("template\\G.bmp", 0);
	char_sample[17] = cvLoadImage("template\\H.bmp", 0);
	char_sample[18] = cvLoadImage("template\\J.bmp", 0);
	char_sample[19] = cvLoadImage("template\\K.bmp", 0);
	char_sample[20] = cvLoadImage("template\\L.bmp", 0);
	char_sample[21] = cvLoadImage("template\\M.bmp", 0);
	char_sample[22] = cvLoadImage("template\\N.bmp", 0);
	char_sample[23] = cvLoadImage("template\\P.bmp", 0);
	char_sample[24] = cvLoadImage("template\\Q.bmp", 0);
	char_sample[25] = cvLoadImage("template\\R.bmp", 0);
	char_sample[26] = cvLoadImage("template\\S.bmp", 0);
	char_sample[27] = cvLoadImage("template\\T.bmp", 0);
	char_sample[28] = cvLoadImage("template\\U.bmp", 0);
	char_sample[29] = cvLoadImage("template\\V.bmp", 0);
	char_sample[30] = cvLoadImage("template\\W.bmp", 0);
	char_sample[31] = cvLoadImage("template\\X.bmp", 0);
	char_sample[32] = cvLoadImage("template\\Y.bmp", 0);
	char_sample[33] = cvLoadImage("template\\Z.bmp", 0);


	//载入汉字模板
	hanzi_sample[0] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\川.bmp", 0);
	hanzi_sample[1] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\鄂.bmp", 0);
	hanzi_sample[2] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\黑.bmp", 0);
	hanzi_sample[3] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\京.bmp", 0);
	hanzi_sample[4] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\辽.bmp", 0);
	hanzi_sample[5] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\琼.bmp", 0);
	hanzi_sample[6] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\湘.bmp", 0);
	hanzi_sample[7] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\粤.bmp", 0);
	hanzi_sample[8] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\浙.bmp", 0);
	//hanzi_sample[0] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\川a.bmp", 0);
	//hanzi_sample[1] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\鄂a.bmp", 0);
	//hanzi_sample[2] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\黑a.bmp", 0);
	//hanzi_sample[3] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\京a.bmp", 0);
	//hanzi_sample[4] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\辽a.bmp", 0);
	//hanzi_sample[5] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\琼a.bmp", 0);
	//hanzi_sample[6] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\湘a.bmp", 0);
	//hanzi_sample[7] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\粤a.bmp", 0);
	//hanzi_sample[8] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\浙a.bmp", 0);	
	//hanzi_sample[9] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\苏a.bmp", 0);
	//hanzi_sample[10] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\藏a.bmp", 0);
	//hanzi_sample[11] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\甘a.bmp", 0);
	//hanzi_sample[12] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\赣a.bmp", 0);
	//hanzi_sample[13] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\桂a.bmp", 0);
	//hanzi_sample[14] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\沪a.bmp", 0);
	//hanzi_sample[15] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\吉a.bmp", 0);
	//hanzi_sample[16] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\冀a.bmp", 0);
	//hanzi_sample[17] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\津a.bmp", 0);
	//hanzi_sample[18] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\晋a.bmp", 0);
	//hanzi_sample[19] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\鲁a.bmp", 0);
	//hanzi_sample[20] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\蒙a.bmp", 0);
	//hanzi_sample[21] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\闽a.bmp", 0);
	//hanzi_sample[22] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\宁a.bmp", 0);
	//hanzi_sample[23] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\青a.bmp", 0);
	//hanzi_sample[24] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\陕a.bmp", 0);
	//hanzi_sample[25] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\皖a.bmp", 0);
	//hanzi_sample[26] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\新a.bmp", 0);
	//hanzi_sample[27] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\渝a.bmp", 0);
	//hanzi_sample[28] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\豫a.bmp", 0);
	//hanzi_sample[29] = cvLoadImage("E:\\LicenseRecognition\\EdgeSearch\\CharacterMatching\\template\\云a.bmp", 0);


	//提取字符样本特征
	for (int i = 0; i < 34; i++)
	{
    
    
		GetFeature(char_sample[i], char_pattern[i]);
	}
	//提取汉字字符特征
	for (int i = 0; i < 9; i++)
	{
    
    
		GetFeature(hanzi_sample[i], hanzi_pattern[i]);
	}
	//提取待识别字符特征
	for (int i = 0; i < 7; i++)
	{
    
    
		GetFeature(dst_image[i], TestSample[i]);
	}

	//进行模板匹配	
	double min = 100000.0;
	for (int num = 0; num < 1; num++)
	{
    
    
		for (int i = 0; i < 9; i++)
		{
    
    
			double diff = 0.0;
			for (int j = 0; j < 25; j++)
			{
    
    
				diff += fabs(TestSample[num].feature[j] - hanzi_pattern[i].feature[j]);
			}
			for (int j = 25; j < 33; j++)
			{
    
    
				diff += fabs(TestSample[num].feature[j] - hanzi_pattern[i].feature[j]) * 9;
			}
			if (diff < min)
			{
    
    
				min = diff;
				TestSample[num].number = i;
			}
		}
	}

	for (int num = 1; num < 7; num++)
	{
    
    
		double min_min = 1000000.0;
		for (int i = 0; i < 34; i++)
		{
    
    
			double diff_diff = 0.0;
			for (int j = 0; j < 25; j++)
			{
    
    
				diff_diff += fabs(TestSample[num].feature[j] - char_pattern[i].feature[j]);
			}
			for (int j = 25; j < 33; j++)
			{
    
    
				diff_diff += fabs(TestSample[num].feature[j] - char_pattern[i].feature[j]);
			}
			if (diff_diff < min_min)
			{
    
    
				min_min = diff_diff;
				TestSample[num].number = i;
			}
		}
	}

	String result = "";//存放识别出的字符

	for (int i = 0; i < 1; i++)
	{
    
    
		switch (TestSample[i].number)
		{
    
    
		case 0:
			result += "川";
			break;
		case 1:
			result += "鄂";
			break;
		case 2:
			result += "黑";
			break;
		case 3:
			result += "京";
			break;
		case 4:
			result += "辽";
			break;
		case 5:
			result += "琼";
			break;
		case 6:
			result += "湘";
			break;
		case 7:
			result += "粤";
			break;
		case 8:
			result += "浙";
			break;
		default:
			cout<<("识别失败")<<endl;
			break;
		}
	}

	for (int i = 1; i < 7; i++)
	{
    
    
		switch (TestSample[i].number)
		{
    
    
		case 0:
			result += "0";
			break;
		case 1:
			result += "1";
			break;
		case 2:
			result += "2";
			break;
		case 3:
			result += "3";
			break;
		case 4:
			result += "4";
			break;
		case 5:
			result += "6";
			break;
		case 6:
			result += "5";
			break;
		case 7:
			result += "7";
			break;
		case 8:
			result += "B";
			break;
		case 9:
			result += "9";
			break;
		case 10:
			result += "A";
			break;
		case 11:
			result += "8";
			break;
		case 12:
			result += "C";
			break;
		case 13:
			result += "D";
			break;
		case 14:
			result += "E";;
			break;
		case 15:
			result += "F";
		case 16:
			result += "G";
			break;
		case 17:
			result += "H";
			break;
		case 18:
			result += "J";
			break;
		case 19:
			result += "K";
			break;
		case 20:
			result += "L";
			break;
		case 21:
			result += "M";
			break;
		case 22:
			result += "N";
			break;
		case 23:
			result += "P";
			break;
		case 24:
			result += "Q";
			break;
		case 25:
			result += "R";
			break;
		case 26:
			result += "S";
			break;
		case 27:
			result += "T";
			break;
		case 28:
			result += "U";
			break;
		case 29:
			result += "U";
			break;
		case 30:
			result += "W";
			break;
		case 31:
			result += "X";
			break;
		case 32:
			result += "Y";
			break;
		case 33:
			result += "Z";
			break;
		default:
			cout<<("识别失败");
			break;
		}
	}
	cout<<"车牌的最终结果为:"<<result<<endl;//显示结果
	system("pause");
	return 0;
}

需要template模板,网上下载即可
模板

结果效果:效果图

五、总结

特别感谢CSDN博主吾理小子、CSDN博主Nine-days、CSDN博主lxx_123456等
也感谢CSDN全体制作OpenCV车牌识别有关博客的博主,为此博客奠定了知识基础,此博客仅供学习使用,希望可以给着急于做出车牌识别的朋友一点灵感,不足之处可以指出,如对读者朋友们有用,希望可以点个赞。

六、感悟

这是我第一次通过观摩借鉴复用各位CSDN大佬们的博客文章以及开源代码整合而成的项目,算是我代码路上一个开始,自此逐渐摆脱了拘泥于书本知识而非亲身实践的学习恶习。搞车牌识别项目的时候会遇到很多很多困难与疑惑,通过找博客和参考其他博主的经验和教训来解决自己的困难,比起我之前遇到困难就退缩,只想白嫖成果要好千万倍(虽然这个也白嫖了很多)。希望可以和CSDN上的兄弟们一起进步共同加油!

猜你喜欢

转载自blog.csdn.net/weixin_50067564/article/details/108640451
今日推荐