概述
使用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]; gray[tt]++; p += 3; } // p += nOffse