opencv 车牌识别

题目三 第7章,代码7-1,用11x11大小的LoG算子对以下图像进行边缘增强,并结合合适的二值化算法、形态学梯度算法以及联通区域标记算法,将图中车牌区域的字符分割出来。

图形

在这里插入图片描述

代码
//head.h

#include<string.h>
#include <stdio.h>
#include <cstdlib>


#include "imgcodecs.hpp"
#include<iostream>
#include<opencv.hpp>
#pragma comment(lib,"opencv_world455.lib")

//#pragma comment(lib, "opencv_world455.lib")


using namespace cv;
using namespace std; 
#pragma once


//图像Log算子运算
Mat LoG_Image(const Mat & image,int kervalue =11, double sigma = 1.0f)
{
	//首先对图像做高斯平滑
	Mat matTemp;
	GaussianBlur(image, matTemp, Size(kervalue, kervalue),\
		sigma, sigma, BORDER_DEFAULT);
	//通过拉斯普斯算子做边缘检测
	Mat laplacian = Mat::zeros(image.rows, image.cols, CV_32FC1);
	Laplacian(matTemp, laplacian, CV_32FC1, 3);
	//求得最大边缘值
	double dblMaxVal = 0;
	minMaxLoc(laplacian, NULL, &dblMaxVal);
	Mat dstImg;
	convertScaleAbs(laplacian, dstImg);

	imwrite("edge.bmp", dstImg);
	imshow("dstImg", dstImg);
	imshow("laplacian", laplacian);

	Mat result = Mat::zeros(image.rows, image.cols, CV_8UC1);
	
	int sum = 0;
	
	result = image;
	//过零点交叉,寻找像素边缘
	for (int i = 1; i < result.rows-1; i++)
	{

		for (int j = 1; j < result.cols-1; j++)
		{
			//cout << dblMaxVal << endl;
			if (laplacian.at<float>(i, j) < 0.1 * dblMaxVal)
			{
				continue;
			}
			//水平,垂直,45度方向,135度方向4个方向过零点判定
			if (laplacian.at<float>(i - 1, j) \
				* laplacian.at<float>(i + 1, j) < 0)
			{
				//cout << "enter1" << endl;
				result.at<uchar>(i, j) = 0;
				//sum++;
			}
			if (laplacian.at<float>(i, j + 1)\
				* laplacian.at<float>(i, j - 1) < 0)
			{
				result.at<uchar>(i, j) = 0;
			/*	cout << "enter2" << endl;
				sum++;*/

			}
			if (laplacian.at<float>(i + 1, j + 1)\
				* laplacian.at<float>(i - 1, j - 1) < 0)
			{
				result.at<uchar>(i, j) = 0;
			/*	cout << "enter3" << endl;
				sum++;*/

			}
			if (laplacian.at<float>(i - 1, j + 1) \
				* laplacian.at<float>(i + 1, j - 1) < 0)
			{
				result.at<uchar>(i, j) = 0;
				/*cout << "enter4" << endl;
				sum++;*/

			}
		}
	}
	/*cout << "面积" << result.rows * result.cols << endl;
	cout << "sum" << sum << endl;*/
	imshow("LoG算子得出", result);
	return result;

		
}

Mat LoG_Image1(const Mat& image, int kervalue = 11, double sigma = 1.0f)
{
	//首先对图像做高斯平滑
	Mat matTemp;
	GaussianBlur(image, matTemp, Size(kervalue, kervalue), \
		sigma, sigma, BORDER_DEFAULT);
	//通过拉斯普斯算子做边缘检测
	Mat laplacian = Mat::zeros(image.rows, image.cols, CV_32FC1);
	Laplacian(matTemp, laplacian, CV_32FC1, 3);
	//求得最大边缘值
	double dblMaxVal = 0;
	minMaxLoc(laplacian, NULL, &dblMaxVal);
	Mat dstImg;
	convertScaleAbs(laplacian, dstImg);

	/*imwrite("edge.bmp", dstImg);
	imshow("dstImg", dstImg);
	imshow("laplacian", laplacian);*/

	Mat result = Mat::zeros(image.rows, image.cols, CV_8UC1);

	int sum = 0;
	
	//过零点交叉,寻找像素边缘
	for (int i = 1; i < result.rows - 1; i++)
	{

		for (int j = 1; j < result.cols - 1; j++)
		{
			//cout << dblMaxVal << endl;
			if (laplacian.at<float>(i, j) < 0.1 * dblMaxVal)
			{
				continue;
			}
			//水平,垂直,45度方向,135度方向4个方向过零点判定
			if (laplacian.at<float>(i - 1, j) \
				* laplacian.at<float>(i + 1, j) < 0)
			{
				//cout << "enter1" << endl;
				result.at<uchar>(i, j) = 255;
				//sum++;
			}
			if (laplacian.at<float>(i, j + 1)\
				* laplacian.at<float>(i, j - 1) < 0)
			{
				result.at<uchar>(i, j) = 255;
				/*	cout << "enter2" << endl;
					sum++;*/

			}
			if (laplacian.at<float>(i + 1, j + 1)\
				* laplacian.at<float>(i - 1, j - 1) < 0)
			{
				result.at<uchar>(i, j) = 255;
				/*	cout << "enter3" << endl;
					sum++;*/

			}
			if (laplacian.at<float>(i - 1, j + 1) \
				* laplacian.at<float>(i + 1, j - 1) < 0)
			{
				result.at<uchar>(i, j) = 255;
				/*cout << "enter4" << endl;
				sum++;*/

			}
		}
	}
	
	return result;


}

//代码7-6
//OTSU方法对256级灰度图像进行二值化
void Otsu(Mat& src,Mat &result)
{
	//Mat src = imread("E:/32/opencv/作业/实验3文件/实验3文件/rice.tif", IMREAD_GRAYSCALE);
	long IPixCnt = src.rows * src.cols;//图像大小
	long histogram[256] = { 0 };//histogram 是灰度直方图
	for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			unsigned char nCurVal = src.at<uchar>(i, j);//访问像素值点
			//cout << (int)nCurVal << endl;
			histogram[nCurVal]++;
		}
	}
	int nThreshold = 0;
	long sum0 = 0, sum1 = 0;//存储前景和背景的灰度总和
	long cnt0 = 0, cnt1 = 0;//存储前景和背景的像素总个数
	double w0 = 0, w1 = 0;//存储前景和背景所占整幅图像的比例
	double u0 = 0, u1 = 0;//存储前景和背景的平均灰度
	double variance = 0;//类间方差
	double maxVariance = 0;//最大类间方差
	for (int i = 0; i < 256; i++)
	{
		sum0 = 0, cnt0 = 0, w0 = 0;
		sum1 = 0, cnt1 = 0, w1 = 0;
		for (int j = 0; j < i; j++) {
			cnt0 += histogram[j];//前景像素总和
			sum0 += j * histogram[j];//前景灰度值总和
		}
		//前景部分平均灰度
		u0 = cnt0 > 0 ? double(sum0) / cnt0 : 0;
		w0 = (double)cnt0 / IPixCnt;//前景部分所占的比例
		for (int j = i; j <= 255; j++)
		{

			cnt1 += histogram[j];//背景像素个数
			sum1 += j * histogram[j];//背景灰度总和

		}
		//背景部分平均灰度
		u1 = cnt1 > 0 ? double(sum1) / cnt1 : 0;
		w1 = 1 - w0;//背景部分所占的比例
		//分割阈值为i时的类间方差
		variance = w0 * w1 * (u0 - u1) * (u0 - u1);
		if (variance > maxVariance) {
			maxVariance = variance;
			nThreshold = i;
		}

	}
	//遍历每个像素,对图像进行二值化
	Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC1);
	for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++) {
			if (src.at<uchar>(i, j) > nThreshold)
			{
				dst.at<uchar>(i, j) = 255;
			}
		}
	}
	//imshow("二值化图像", dst);
	result = dst;
	//waitKey(0);

}
//代码7-7
//三角法二值化原理
void TriangleBinary(const Mat &src,Mat &result)
{
	//Mat src = imread("defective_weld.tif", IMREAD_GRAYSCALE);
	long lPixCnt = src.rows * src.cols;
	long histogram[256] = { 0 };
	//histogram是灰度直方图
	for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			unsigned char nCurval = src.at<uchar>(i, j);
			histogram[nCurval]++;
		}
	}
	//左右边界
	int left_bound = 0, right_bound = 0;
	//直方图最高峰和相应的灰度值
	int max_ind = 0, maxPeak = 0;
	int temp;
	bool isflipped = false;
	//找到最左边零的位置
	for (int i = 0; i < 256; i++)
	{
		if (histogram[i] > 0) {
			left_bound = i;
			break;
		}
	}
	//位置再移动一个步长,节日最左侧零的位置
	if (left_bound > 0)
	{
		left_bound--;
	}

	//找到最右边零的位置
	for (int i = 255; i>0; i--)
	{
		if (histogram[i] > 0) {
			right_bound = i;
			break;
		}
	}
	//位置再移动一个步长,节日最右侧零的位置
	if (right_bound <255)
	{
		right_bound++;
	}
	//在直方图上寻找最亮的点Hmax
	for (int i = 0; i < 256; i++)
	{
		if (histogram[i] > maxPeak)
		{
			maxPeak = histogram[i];
			max_ind = i;
		}
	}
	//如果最大值落在靠左侧这样就无法满足三角法求阈值
	//所以要检测最大值是否靠近左侧
	//如果靠近左侧要通过翻转到右侧位置
	if (max_ind - left_bound < right_bound - max_ind)
	{
		isflipped = true;
		int i = 0;
		int j = 255;
		//左右交换
		while (i < j) {
			temp = histogram[i];
			histogram[i] = histogram[j];
			histogram[j] = temp;
			i++;
			j--;
		}

	}
	//计算求得阈值
	double thresh = left_bound;
	double maxDist = 0, tempDist;
	double peakIdxBound = left_bound - max_ind;
	for (int i = left_bound + 1; i < max_ind; i++)
	{
		//计算距离
		tempDist = maxPeak * i + peakIdxBound * histogram[i];
		if (tempDist < tempDist)
		{
			maxDist = tempDist;
			thresh = i;

		}
	}
	thresh--;
	if (isflipped)
	{
		thresh = 255 - thresh;

	}
	//遍历每一个像素,对图像进行二值化
	Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC1);
	for (int i = 0; i < src.rows; i++) {
		for (int j = 0; j < src.cols; j++) {
			if (src.at<uchar>(i, j) > thresh)
			{
				dst.at<uchar>(i, j) = 255;
			}

		}
	}imshow("二值化", dst);
	result = dst;


}

//代码7-8全局阈值化函数threshold的用法
void GLobalBinbary()
{
	Mat src = imread("E:/32/opencv/作业/实验3文件/实验3文件/00_20180929163745.jpg", \
		IMREAD_GRAYSCALE);
	Mat binImg;
	//采用OTSU方法计算阈值,前景设置为白色
	threshold(src, binImg, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
	imshow("OTSU二值化", binImg);
	//采用三角法计算阈值,前景设置为黑色
	threshold(src, binImg, 0, 255, THRESH_BINARY_INV | THRESH_TRIANGLE);
	imshow("三角法二值化", binImg);
	waitKey(0);

}


//代码7-9 局部自适应二值化实例
void AdaptiveBinary(const Mat &src,Mat  &result)
{
	//Mat src = imread("E:/32/opencv/作业/实验3文件/实验3文件/00_20180929163745.jpg",\
		IMREAD_GRAYSCALE);
	Mat binImg;
	//采用OTSU方法计算阈值,前景设置为白色
	threshold(src, binImg, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
	//imshow("OTSU二值化", binImg);

	adaptiveThreshold(src, binImg, 255, ADAPTIVE_THRESH_GAUSSIAN_C, \
		THRESH_BINARY_INV, 7, 10);
	result = binImg;
	//imshow("局部自适应二值化结果", binImg);
	

}

void AdaptiveBinary_otsu(const Mat& src, Mat& result)
{
	//Mat src = imread("E:/32/opencv/作业/实验3文件/实验3文件/00_20180929163745.jpg",\
		IMREAD_GRAYSCALE);
	Mat binImg;
	//采用OTSU方法计算阈值,前景设置为白色
	threshold(src, binImg, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
	//imshow("OTSU二值化", binImg);

	/*adaptiveThreshold(src, binImg, 255, ADAPTIVE_THRESH_GAUSSIAN_C, \
		THRESH_BINARY_INV, 7, 10);
	
	imshow("局部自适应二值化结果", binImg);*/
	result = binImg;


}



//图像放大
void fun1(cv::Mat& src, double kx, double ky, Mat& result)
{
	int  row = src.rows * kx;
	int  col = src.cols * ky;

	cv::Mat dst(row, col, src.type());
	for (int i = 0; i < row; i++)
	{
		int srx = i / kx;
		for (int j = 0; j < col; j++)
		{
			int sry = j / ky;
			dst.at<cv::Vec3b>(i, j) = src.at<cv::Vec3b>(srx, sry);
		}
	}
	result = dst;
	

}


#include"head.h"


void imgContoursROI()//轮廓roi
{

	//1读取图像
	Mat src1, src, dst, img_gaussian;
	//rgb图像
	src1 = imread("E:/32/opencv/作业/实验3文件/实验3文件/00_20180929163745.jpg");
	//灰度图像
	src = imread("E:/32/opencv/作业/实验3文件/实验3文件/00_20180929163745.jpg", \
		IMREAD_GRAYSCALE);
	//复制一个图像
	Mat copyImg = src1.clone();

	imshow("src", src);


	// 图像预处理


	//图片降噪
	//GaussianBlur( InputArray src, OutputArray dst, Size ksize,
	/*double sigmaX, double sigmaY = 0,
		int borderType = BORDER_DEFAULT );*/
		//2高斯平滑
	Mat matTemp;
	int kervalue = 11; double sigma = 1.0f;
	GaussianBlur(src, matTemp, Size(kervalue, kervalue), \
		sigma, sigma, BORDER_DEFAULT);
	//imshow("Gaussian_Blur2", matTemp);


	//3直方图均衡化,增加对比度
	Mat out;
	equalizeHist(matTemp, out);
	/*namedWindow("经过直方图均化后处理");
	imshow("经过直方图均化后处理", out);*/

	//4二值化
	Mat diff;
	Otsu(out, diff);


	//5画轮廓

	//定义轮廓和层次结构
	vector<vector<Point> >contours;
	vector<Vec4i> hierarchy;
	//查找轮廓
	Mat result;
	findContours(diff, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);

	//遍历所有的顶层的轮廓,绘制每个连接组件颜色

	int index = 0;

	for (; index >= 0; index = hierarchy[index][0])
	{
		//Scalar color(rand() & 255, rand() & 255, rand() & 255);
		Scalar color(0, 0, 0);
		if (contourArea(contours[index]) > 2000 && contourArea(contours[index]) < 2500) {
			drawContours(src1, contours, index, color, FILLED, 8, hierarchy);
			Rect rect = boundingRect(contours[index]);
			//rectangle(src1, Rect(rect.x, rect.y, rect.width, rect.height), Scalar(0, 255, 0), 2); //将感兴趣区域框出来
			imshow("src1", src1);
			result = copyImg(Rect((rect.x + 5), (rect.y + 3), (rect.width - 10), (rect.height - 8)));
			//result = copyImg(recta);
			imshow("result", result);
			imwrite("E:/32/opencv/作业/实验3文件/实验3文件/license.jpg", result);
			fun1(result, 5, 5, result);
			imshow("maxresult", result);
			imwrite("E:/32/opencv/作业/实验3文件/实验3文件/maxlicense.jpg", result);
			src = imread("E:/32/opencv/作业/实验3文件/实验3文件/license.jpg", IMREAD_GRAYSCALE);
			//局部自适应二值化图像
			AdaptiveBinary(src, result);
			//imshow("src_bin", src_bin);
			//
			result = LoG_Image1(src);


		}




	}

}

void	characterSegmentation()
{
	Mat src = imread("E:/32/opencv/作业/实验3文件/实验3文件/maxlicense.jpg", IMREAD_GRAYSCALE);
	Mat src1 = imread("E:/32/opencv/作业/实验3文件/实验3文件/maxlicense.jpg");
	//imshow("src", src);
	//imshow("src1", src1);


	Mat dst, src_otsu, src_bin, src_dilate, src_erode, src_LoG, src_label, src_bin2, src_close, src_extract;
	//imshow("grayed image", src);

	/*cout << "good";
	extractImg(src, dst);*/


	//局部自适应二值化图像
	AdaptiveBinary_otsu(src, src_bin);
	src_LoG = src_bin;

	bitwise_not(src_bin, src_bin);
	//imshow("反色之后", src_bin);
	Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
	//---------【2】进行形态学梯度操作---------
	morphologyEx(src_bin, src_bin, MORPH_GRADIENT, element);
	//imshow("形态学", src_bin);
	//
	/*src_LoG = LoG_Image1(src);*/

		//定义轮廓和层次结构
	vector<vector<Point> >contours;
	vector<Vec4i> hierarchy;
	//查找轮廓
	Mat result;
	findContours(src_bin, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);

	//遍历所有的顶层的轮廓,随机颜色绘制每个连接组件颜色

	int index = 0;

	for (; index >= 0; index = hierarchy[index][0])
	{
		//Scalar color(rand() & 255, rand() & 255, rand() & 255);
		Scalar color(0, 0, 0);
		/*if ((contourArea(contours[index]) > 300 && contourArea(contours[index]) <10000) ) {*/
		if ((contourArea(contours[index]) > 350 && contourArea(contours[index]) < 800) || \
			(contourArea(contours[index]) > 1000 && contourArea(contours[index]) < 5000)) {
			drawContours(src_bin, contours, index, color, FILLED, 8, hierarchy);
			Rect rect = boundingRect(contours[index]);
			rectangle(src1, Rect(rect.x, rect.y, rect.width, rect.height), Scalar(0, 255, 0), 2); //将感兴趣区域框出来
			imshow("src1", src1);
			result = src1(rect);
			//result = copyImg(recta);
			//imshow("result", result);
			//imwrite("E:/32/opencv/作业/实验3文件/实验3文件/license.jpg", result);
			//fun1(result, 5, 5, result);
			//imshow("maxresult", result);
			//src = imread("E:/32/opencv/作业/实验3文件/实验3文件/license.jpg", IMREAD_GRAYSCALE);
			//局部自适应二值化图像
			//AdaptiveBinary(src, result);
			//imshow("src_bin", src_bin);
			//
			/*result = LoG_Image1(src);*/
			cout << index << "      ";

			cout << contourArea(contours[index]) << endl;
			imshow("number" + to_string(index), result);
			imwrite("number" + to_string(index) + ".jpg", result);

		}
	}
}

void main()
{
	//选择roi区域region of interest
	imgContoursROI();
	//字符分割
	characterSegmentation();
	waitKey(0);
}
结果

在这里插入图片描述

参考链接
  1. roi选取

    https://www.cnblogs.com/skyfsm/p/6892746.html

  2. 图像放大

    https://blog.csdn.net/ljl199584/article/details/108599044?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165259738516782246453478%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165259738516782246453478&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-1-108599044-null-null.142v9pc_search_result_control_group,157v4control&utm_term=%E5%9B%BE%E5%83%8F%E7%AD%89%E6%AF%94%E6%94%BE%E5%A4%A7opencv&spm=1018.2226.3001.4187

  3. 字符分割

    https://blog.csdn.net/weixin_44613063/article/details/109563843?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-1-109563843-blog-8947349.pc_relevant_paycolumn_v3&spm=1001.2101.3001.4242.2&utm_relevant_index=4

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值