我设法使 emgucv 的车牌识别示例有效。但是,我没有获得所需的 OCR 识别来识别我所在位置的车辆车牌号。
这是代码
public class LicensePlateDetector : DisposableObject
{
private Tesseract _ocr;
/// <summary>
/// Create a license plate detector
/// </summary>
public LicensePlateDetector()
{
//create OCR
_ocr = new Tesseract();
_ocr.SetVariable("tessedit_char_whitelist", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");
_ocr.Init(@"D:\tessdata", "eng", false);
}
public List<String> DetectLicensePlate(
Image<Bgr, byte> img,
List<Image<Gray, Byte>> licensePlateImagesList,
List<Image<Gray, Byte>> filteredLicensePlateImagesList,
List<MCvBox2D> detectedLicensePlateRegionList)
{
List<String> licenses = new List<String>();
using (Image<Gray, byte> gray = img.Convert<Gray, Byte>())
//using (Image<Gray, byte> gray = GetWhitePixelMask(img))
using (Image<Gray, Byte> canny = new Image<Gray, byte>(gray.Size))
using (MemStorage stor = new MemStorage())
{
canny.ThresholdBinary(new Gray(50), new Gray(255));
CvInvoke.cvCanny(gray, canny, 100, 50, 3);
canny.Dilate(20);
Contour<Point> contours = canny.FindContours(
Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_TREE,
stor);
FindLicensePlate(contours, gray, canny, licensePlateImagesList, filteredLicensePlateImagesList, detectedLicensePlateRegionList, licenses);
}
return licenses;
}
private void FindLicensePlate(
Contour<Point> contours, Image<Gray, Byte> gray, Image<Gray, Byte> canny,
List<Image<Gray, Byte>> licensePlateImagesList, List<Image<Gray, Byte>> filteredLicensePlateImagesList, List<MCvBox2D> detectedLicensePlateRegionList,
List<String> licenses)
{
for (; contours != null; contours = contours.HNext)
{
//int numberOfChildren = GetNumberOfChildren(contours);
//if it does not contains any children (charactor), it is not a license plate region
//if (numberOfChildren == 0) continue;
Contour<Point> approxContour = contours.ApproxPoly(contours.Perimeter * 0.05, contours.Storage);
if (approxContour.Area > 100 && approxContour.Total == 4)
{
//img.Draw(contours, new Bgr(Color.Red), 1);
if (!IsParallelogram(approxContour.ToArray()))
{
Contour<Point> child = contours.VNext;
if (child != null)
FindLicensePlate(child, gray, canny, licensePlateImagesList, filteredLicensePlateImagesList, detectedLicensePlateRegionList, licenses);
continue;
}
MCvBox2D box = contours.GetMinAreaRect();
if (box.angle < -45.0)
{
float tmp = box.size.Width;
box.size.Width = box.size.Height;
box.size.Height = tmp;
box.angle += 90.0f;
}
else if (box.angle > 45.0)
{
float tmp = box.size.Width;
box.size.Width = box.size.Height;
box.size.Height = tmp;
box.angle -= 90.0f;
}
double whRatio = (double)box.size.Width / box.size.Height;
if (!(3.0 < whRatio && whRatio < 8.0))
//if (!(1.0 < whRatio && whRatio < 2.0))
{ //if the width height ratio is not in the specific range,it is not a license plate
//However we should search the children of this contour to see if any of them is a license plate
Contour<Point> child = contours.VNext;
if (child != null)
FindLicensePlate(child, gray, canny, licensePlateImagesList, filteredLicensePlateImagesList, detectedLicensePlateRegionList, licenses);
continue;
}
using (Image<Gray, Byte> tmp1 = gray.Copy(box))
//resize the license plate such that the front is ~ 10-12. This size of front results in better accuracy from tesseract
using (Image<Gray, Byte> tmp2 = tmp1.Resize(240, 180, Emgu.CV.CvEnum.INTER.CV_INTER_CUBIC, true))
{
//removes some pixels from the edge
int edgePixelSize = 2;
tmp2.ROI = new Rectangle(new Point(edgePixelSize, edgePixelSize), tmp2.Size - new Size(2 * edgePixelSize, 2 * edgePixelSize));
Image<Gray, Byte> plate = tmp2.Copy();
Image<Gray, Byte> filteredPlate = FilterPlate(plate);
//Tesseract.Charactor[] words;
List<Word> words;
StringBuilder strBuilder = new StringBuilder();
using (Bitmap tmp = filteredPlate.Bitmap)
{
//_ocr.Recognize(tmp);
words = _ocr.DoOCR(tmp, filteredPlate.ROI);
if (words.Count == 0) continue;
for (int i = 0; i < words.Count; i++)
{
strBuilder.Append(words[i].Text);
}
}
licenses.Add(strBuilder.ToString());
licensePlateImagesList.Add(plate);
filteredLicensePlateImagesList.Add(filteredPlate);
detectedLicensePlateRegionList.Add(box);
}
}
}
}
private static bool IsParallelogram(Point[] pts)
{
LineSegment2D[] edges = PointCollection.PolyLine(pts, true);
double diff1 = Math.Abs(edges[0].Length - edges[2].Length);
double diff2 = Math.Abs(edges[1].Length - edges[3].Length);
if (diff1 / edges[0].Length <= 0.05 && diff1 / edges[2].Length <= 0.05
&& diff2 / edges[1].Length <= 0.05 && diff2 / edges[3].Length <= 0.05)
{
return true;
}
return false;
}
private static Image<Gray, Byte> FilterPlate(Image<Gray, Byte> plate)
{
Image<Gray, Byte> thresh = plate.ThresholdBinaryInv(new Gray(120), new Gray(255));
using (Image<Gray, Byte> plateMask = new Image<Gray, byte>(plate.Size))
using (Image<Gray, Byte> plateCanny = plate.Canny(new Gray(100), new Gray(50)))
using (MemStorage stor = new MemStorage())
{
plateMask.SetValue(255.0);
for (
Contour<Point> contours = plateCanny.FindContours(
Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_EXTERNAL,
stor);
contours != null; contours = contours.HNext)
{
Rectangle rect = contours.BoundingRectangle;
if (rect.Height > (plate.Height >> 1))
{
rect.X -= 1; rect.Y -= 1; rect.Width += 2; rect.Height += 2;
rect.Intersect(plate.ROI);
plateMask.Draw(rect, new Gray(0.0), -1);
}
}
thresh.SetValue(0, plateMask);
}
thresh._Erode(1);
thresh._Dilate(1);
return thresh;
}
private static int GetNumberOfChildren(Contour<Point> contours)
{
Contour<Point> child = contours.VNext;
if (child == null) return 0;
int count = 0;
while (child != null)
{
count++;
child = child.HNext;
}
return count;
}
protected override void DisposeObject()
{
_ocr.Dispose();
}
}
这是带有标准车牌号的当地汽车的图片。我使用 openfiledialog 来获取它。
但是,我的程序无法识别任何字符添加全部。我得到的最接近的是这张照片。但这不是我想要识别的图片
是由于图像大小还是我需要做进一步的图像预处理。提前致谢