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




二、实现过程
①、图像处理模块
图像处理使用了灰度化、灰度均衡化、高斯滤波、边缘检测、二值化等技术实现了车牌图像的定位和检测,再通过对已有图片的特征匹配来实现车牌图片的识别。
//车辆图片处理事件
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];

本文介绍了使用C#进行摄像头车牌图像识别的实现过程,包括图像处理模块,如灰度化、边缘检测和二值化等技术进行车牌定位和检测,以及特征训练模块,用于精确提取字符图像进行特征匹配。摄像头模块则负责拍摄和处理图像。
最低0.47元/天 解锁文章
2173

被折叠的 条评论
为什么被折叠?



