摄像头车牌图像识别

技术:C#+WinForm+Windows API
运行环境:Visual Studio 2015

概述

使用C#进行车牌检测和车牌识别,车牌检测包括图像分割和特征提取,车牌识别是指对检测到的车牌进行内容识别。利用 Visual Studio 2015 集成开发环境,采用System.Drawing命名空间的类来处理图片、Windows API控制摄像头。

详细

一、运行效果

000.png

111.png

333.png

222.png

二、实现过程

①、图像处理模块

图像处理使用了灰度化、灰度均衡化、高斯滤波、边缘检测、二值化等技术实现了车牌图像的定位和检测,再通过对已有图片的特征匹配来实现车牌图片的识别。

//车辆图片处理事件
private void t灰度化()
{

	if (m_Bitmap != null)
	{
		int tt = 0;
		for (int i = 0; i < 256; i++)//清掉数组gray里的数据
		{
			gray[i] = 0;
		}
		for (int i = 0; i < 256; i++)//清掉数组rr里的数据
		{
			rr[i] = 0;
		}
		for (int i = 0; i < 256; i++)//清掉数组gg里的数据
		{
			gg[i] = 0;
		}
		for (int i = 0; i < 256; i++)//清掉数组bb里的数据
		{
			bb[i] = 0;
		}
		BitmapData bmData = m_Bitmap.LockBits(new Rectangle(0, 0, m_Bitmap.Width, m_Bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
		int stride = bmData.Stride;//获取或设置 Bitmap 对象的跨距宽度(也称为扫描宽度)。 
		System.IntPtr Scan0 = bmData.Scan0;//获取或设置位图中第一个像素数据的地址。 它也可以看成是位图中的第一个扫描行
		unsafe
		{
			byte* p = (byte*)(void*)Scan0;
			int nOffset = stride - m_Bitmap.Width * 3;
			byte red, green, blue;
			int nWidth = m_Bitmap.Width;
			int nHeight = m_Bitmap.Height;
			for (int y = 0; y < nHeight; ++y)
			{
				for (int x = 0; x < nWidth; ++x)
				{
					blue = p[0];
					green = p[1];
					red = p[2];
					tt = p[0] = p[1] = p[2] = (byte)(.299 * red + .587 * green + .114 * blue);
					rr[red]++;
					gg[green]++;
					bb[blue]++;
					gray[tt]++;   //统计灰度值为tt的象素点数目  
					p += 3;
				}
				p += nOffset;
			}
		}
		m_Bitmap.UnlockBits(bmData);
		flag = 1;
		graydo();
	}

}
private void t灰度均衡化()
{

	if (m_Bitmap != null)
	{
		BitmapData bmData = m_Bitmap.LockBits(new Rectangle(0, 0, m_Bitmap.Width, m_Bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
		//加入内存进行处理
		int stride = bmData.Stride;
		System.IntPtr Scan0 = bmData.Scan0;//扫描的第一行
		int tt = 0;
		int[] SumGray = new int[256];
		for (int i = 0; i < 256; i++)
		{
			SumGray[i] = 0;
		}
		unsafe
		{
			byte* p = (byte*)(void*)Scan0;
			int nOffset = stride - m_Bitmap.Width * 3;
			int nHeight = m_Bitmap.Height;
			int nWidth = m_Bitmap.Width;
			SumGray[0] = gray[0];//灰度均衡化 
			for (int i = 1; i < 256; ++i)//灰度级频度数累加                         
				SumGray[i] = SumGray[i - 1] + gray[i];
			for (int i = 0; i < 256; ++i) //计算调整灰度值     频率乘以灰度总级数得出该灰度变换后的灰度级                   
				SumGray[i] = (int)(SumGray[i] * 255 / count);
			for (int i = 0; i < 256; i++)
			{
				gray[i] = 0;
			}
			for (int y = 0; y < nHeight; ++y)
			{
				for (int x = 0; x < nWidth; ++x)
				{
					tt = p[0] = p[1] = p[2] = (byte)(SumGray[p[0]]);
					gray[tt]++;
					p += 3;
				}
				p += nOffset;
			}
		}
		m_Bitmap.UnlockBits(bmData);
		flag = 1;
		graydo();
	}
}
private void t高斯滤波()
{
	if (m_Bitmap != null)
	{
		BitmapData bmData = m_Bitmap.LockBits(new Rectangle(0, 0, m_Bitmap.Width, m_Bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
		for (int i = 0; i < 256; i++)
		{
			gray[i] = 0;
		}
		unsafe
		{
			int stride = bmData.Stride;
			System.IntPtr Scan0 = bmData.Scan0;
			byte* p = (byte*)(void*)Scan0;
			byte* pp;
			int tt;
			int nOffset = stride - m_Bitmap.Width * 3;
			int nWidth = m_Bitmap.Width;
			int nHeight = m_Bitmap.Height;
			long sum = 0;
			int[,] gaussianMatrix = { { 1, 2, 3, 2, 1 }, { 2, 4, 6, 4, 2 }, { 3, 6, 7, 6, 3 }, { 2, 4, 6, 4, 2 }, { 1, 2, 3, 2, 1 } };//高斯滤波器所选的n=5模板
			for (int y = 0; y < nHeight; ++y)
			{
				for (int x = 0; x < nWidth; ++x)
				{
					if (!(x <= 1 || x >= nWidth - 2 || y <= 1 || y >= nHeight - 2))
					{
						pp = p;
						sum = 0;
						int dividend = 79;
						for (int i = -2; i <= 2; i++)
							for (int j = -2; j <= 2; j++)
							{
								pp += (j * 3 + stride * i);
								sum += pp[0] * gaussianMatrix[i + 2, j + 2];
								if (i == 0 && j == 0)
								{
									if (pp[0] > 240)//如果模板中心的灰度大于240
									{
										sum += p[0] * 30;
										dividend += 30;
									}
									else if (pp[0] > 230)
									{
										sum += pp[0] * 20;
										dividend += 20;
									}
									else if (pp[0] > 220)
									{
										sum += p[0] * 15;
										dividend += 15;
									}
									else if (pp[0] > 210)
									{
										sum += pp[0] * 10;
										dividend += 10;
									}
									else if (p[0] > 200)
									{
										sum += pp[0] * 5;
										dividend += 5;
									}
								}
								pp = p;
							}
						sum = sum / dividend;
						if (sum > 255)
						{
							sum = 255;
						}
						p[0] = p[1] = p[2] = (byte)(sum);
					}
					tt = p[0];
					gray[tt]++;
					p += 3;
				}
				p += nOffset;
			}
		}
		flag = 1;
		m_Bitmap.UnlockBits(bmData);
		graydo();
	}
}

//定位处理事件
private void sobel边缘检测()
{
	if (m_Bitmap != null)
	{
		BitmapData bmData = m_Bitmap.LockBits(new Rectangle(0, 0, m_Bitmap.Width, m_Bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
		float valve = 67;
		for (int i = 0; i < 256; i++)
		{
			gray[i] = 0;
		}
		unsafe
		{
			int stride = bmData.Stride;
			System.IntPtr Scan0 = bmData.Scan0;
			byte* p = (byte*)(void*)Scan0;
			byte* pp;
			int tt;
			int nOffset = stride - m_Bitmap.Width * 3;
			int nWidth = m_Bitmap.Width;
			int nHeight = m_Bitmap.Height;
			int Sx = 0;
			int Sy = 0;
			//	float max = 0;
			double sumM = 0;
			double sumCount = 0;
			int[] marginalMx = { -1, 0, 1, -2, 0, 2, -1, 0, 1 };   //sobel模板
			int[] marginalMy = { 1, 2, 1, 0, 0, 0, -1, -2, -1 };
			int[,] dlta = new int[nHeight, nWidth];
			for (int y = 0; y < nHeight; ++y)      //sobel算子
			{
				for (int x = 0; x < nWidth; ++x)
				{
					if (!(x <= 0 || x >= nWidth - 1 || y <= 0 || y >= nHeight - 1))
					{
						pp = p;
						Sx = 0;
						Sy = 0;
						for (int i = -1; i <= 1; i++)
							for (int j = -1; j <= 1; j++)
							{
								pp += (j * 3 + stride * i);
								Sx += pp[0] * marginalMx[(i + 1) * 3 + j + 1];
								Sy += pp[0] * marginalMy[(i + 1) * 3 + j + 1];
								pp = p;
							}
						m[y, x] = (int)(Math.Sqrt(Sx * Sx + Sy * Sy));
						if (m[y, x] > valve / 2) //增强白点
						{
							if (p[0] > 240)
							{
								m[y, x] += valve;
							}
							else if (p[0] > 220)
							{
								m[y, x] += (float)(valve * 0.8);
							}
							else if (p[0] > 200)
							{
								m[y, x] += (float)(valve * 0.6);
							}
							else if (p[0] > 180)
							{
								m[y, x] += (float)(valve * 0.4);
							}
							else if (p[0] > 160)
							{
								m[y, x] += (float)(valve * 0.2);
							}
						}
						float tan;
						if (Sx != 0)
						{
							tan = Sy / Sx;
						}
						else tan = 10000;
						if (-0.41421356 <= tan && tan < 0.41421356)//角度为-22.5度到22.5度之间
						{
							dlta[y, x] = 0; //	m[y,x]+=valve;                                 
						}
						else if (0.41421356 <= tan && tan < 2.41421356)//角度为22.5度到67.5度之间
						{
							dlta[y, x] = 1; //m[y,x] = 0;
						}
						else if (tan >= 2.41421356 || tan < -2.41421356)//角度为67.5度到90度之间或-90度到-67.5度
						{
							dlta[y, x] = 2;    //	m[y,x]+=valve;
						}
						else
						{
							dlta[y, x] = 3;//m[y,x] = 0;     
						}
					}
					else
						m[y, x] = 0;
					p += 3;
					if (m[y, x] > 0)
					{
						sumCount++;
						sumM += m[y, x];
					}
				}
				p += nOffset;

			}
			p = (byte*)(void*)Scan0; //非极大值抑制和阀值
			for (int y = 0; y < nHeight; ++y)
			{
				for (int x = 0; x < nWidth; ++x)
				{
					if (m[y, x] > sumM / sumCount * 1.2)
					{
						p[0] = p[1] = p[2] = (byte)(m[y, x]); //m[y,x]=1;                            
					}
					else
					{
						m[y, x] = 0;
						p[0] = p[1] = p[2] = 0;
					}
					if (x >= 1 && x <= nWidth - 1 && y >= 1 && y <= nHeight - 1 && m[y, x] > valve)
					{
						switch (dlta[y, x])
						{
							case 0:
								if (m[y, x] >= m[y, x - 1] && m[y, x] >= m[y, x + 1])//水平边缘
								{
									p[0] = p[1] = p[2] = 255;
								}
								break;

							case 1:
								if (m[y, x] >= m[y + 1, x - 1] && m[y, x] >= m[y - 1, x + 1])//正斜45度边缘
								{
									p[0] = p[1] = p[2] = 255;
								}
								break;

							case 2:
								if (m[y, x] >= m[y - 1, x] && m[y, x] >= m[y + 1, x])//垂直边缘
								{
									p[0] = p[1] = p[2] = 255;
								}
								break;

							case 3:
								if (m[y, x] >= m[y + 1, x + 1] && m[y, x] >= m[y - 1, x - 1])//反斜45度边缘
								{
									p[0] = p[1] = p[2] = 255;
								}
								break;
						}
					}
					if (p[0] == 255)
					{
						m[y, x] = 1;
					}
					else
					{
						m[y, x] = 0;
						p[0] = p[1] = p[2] = 0;
					}

					tt = p[0];
					gray[tt]++;
					p += 3;
				}
				//  p += nOffset;
阅读终点,创作起航,您可以撰写心得或摘录文章要点写篇博文。去创作
  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现车牌图像识别功能的一般步骤如下: 1. 采集车牌图像数据,可以使用摄像头或者从已有的数据集中获取。 2. 预处理图像,包括图像增强、降噪、图像分割等操作。 3. 使用机器学习算法或深度学习框架来训练模型,以识别车牌。 4. 对于新的图像,将其输入到模型中进行预测,输出车牌号码。 下面是一个基于Java实现车牌图像识别的简单示例: 1. 采集车牌图像数据。 这里我们使用JavaCV库来读取摄像头捕获的视频流,并将每一帧图像保存到本地文件中。代码示例如下: ``` import org.bytedeco.javacv.*; import org.bytedeco.opencv.opencv_core.*; public class CaptureImage { public static void main(String[] args) throws Exception { FrameGrabber grabber = new OpenCVFrameGrabber(0); grabber.start(); int i = 0; while (i < 10) { // 捕获10张图片 Mat frame = grabber.grab(); String filename = "image" + i + ".jpg"; imwrite(filename, frame); i++; Thread.sleep(1000); // 每隔1秒钟捕获一张图片 } grabber.stop(); } } ``` 2. 预处理图像。 这里我们使用OpenCV库来进行图像预处理,包括高斯滤波、灰度化、二值化等操作。代码示例如下: ``` import org.bytedeco.opencv.opencv_core.*; import org.bytedeco.opencv.global.opencv_imgproc; public class PreprocessImage { public static void main(String[] args) { Mat image = imread("image0.jpg", CV_LOAD_IMAGE_COLOR); Mat gray = new Mat(); cvtColor(image, gray, COLOR_BGR2GRAY); GaussianBlur(gray, gray, new Size(3, 3), 0); threshold(gray, gray, 0, 255, THRESH_BINARY_INV + THRESH_OTSU); imwrite("preprocess.jpg", gray); } } ``` 3. 训练模型。 训练模型需要一定的算法知识和数据集,这里不做过多介绍。 4. 图像识别。 这里我们使用Tesseract OCR引擎来识别车牌号码。代码示例如下: ``` import net.sourceforge.tess4j.*; public class RecognizeImage { public static void main(String[] args) { ITesseract tesseract = new Tesseract(); tesseract.setDatapath("/usr/share/tesseract-ocr/4.00/tessdata"); // 设置tessdata目录 tesseract.setLanguage("chi_sim+eng"); // 设置识别语言为中文和英文 File imageFile = new File("preprocess.jpg"); try { String result = tesseract.doOCR(imageFile); System.out.println(result); // 输出识别结果 } catch (TesseractException e) { e.printStackTrace(); } } } ``` 以上就是一个基于Java实现车牌图像识别的简单示例,实际应用中需要根据具体需求进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值