C# OpenCV6 -車牌識別
阿新 • • 發佈:2018-12-02
1. 定義車牌識別器:
2. 使用Tesseract作為ocr引擎
3. 識別
4. 噪音處理
車牌識別類:
/// <summary> /// A simple license plate detector /// </summary> public class LicensePlateDetector : DisposableObject { /// <summary> /// The OCR engine /// </summary> private Tesseract _ocr; /// <summary> /// Create a license plate detector /// </summary> /// <param name="dataPath"> /// The datapath must be the name of the parent directory of tessdata and /// must end in / . Any name after the last / will be stripped. /// </param> public LicensePlateDetector(String dataPath) { //create OCR engine _ocr = new Tesseract(dataPath, "eng", OcrEngineMode.TesseractCubeCombined); _ocr.SetVariable("tessedit_char_whitelist", "ABCDEFGHIJKLMNOPQRSTUVWXYZ-1234567890"); } /// <summary> /// Detect license plate from the given image /// </summary> /// <param name="img">The image to search license plate from</param> /// <param name="licensePlateImagesList">A list of images where the detected license plate regions are stored</param> /// <param name="filteredLicensePlateImagesList">A list of images where the detected license plate regions (with noise removed) are stored</param> /// <param name="detectedLicensePlateRegionList">A list where the regions of license plate (defined by an MCvBox2D) are stored</param> /// <returns>The list of words for each license plate</returns> public List<String> DetectLicensePlate( IInputArray img, List<IInputOutputArray> licensePlateImagesList, List<IInputOutputArray> filteredLicensePlateImagesList, List<RotatedRect> detectedLicensePlateRegionList) { List<String> licenses = new List<String>(); using (Mat gray = new Mat()) using (Mat canny = new Mat()) using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint()) { CvInvoke.CvtColor(img, gray, ColorConversion.Bgr2Gray); CvInvoke.Canny(gray, canny, 100, 50, 3, false); int[,] hierachy = CvInvoke.FindContourTree(canny, contours, ChainApproxMethod.ChainApproxSimple); FindLicensePlate(contours, hierachy, 0, gray, canny, licensePlateImagesList, filteredLicensePlateImagesList, detectedLicensePlateRegionList, licenses); } return licenses; } private static int GetNumberOfChildren(int[,] hierachy, int idx) { //first child idx = hierachy[idx,2]; if (idx < 0) return 0; int count = 1; while (hierachy[idx,0] > 0) { count++; idx = hierachy[idx,0]; } return count; } private void FindLicensePlate( VectorOfVectorOfPoint contours, int[,] hierachy, int idx, IInputArray gray, IInputArray canny, List<IInputOutputArray> licensePlateImagesList, List<IInputOutputArray> filteredLicensePlateImagesList, List<RotatedRect> detectedLicensePlateRegionList, List<String> licenses) { for (; idx >= 0; idx = hierachy[idx,0]) { int numberOfChildren = GetNumberOfChildren(hierachy, idx); //if it does not contains any children (charactor), it is not a license plate region if (numberOfChildren == 0) continue; using (VectorOfPoint contour = contours[idx]) { if (CvInvoke.ContourArea(contour) > 400) { if (numberOfChildren < 3) { //If the contour has less than 3 children, it is not a license plate (assuming license plate has at least 3 charactor) //However we should search the children of this contour to see if any of them is a license plate FindLicensePlate(contours, hierachy, hierachy[idx, 2], gray, canny, licensePlateImagesList, filteredLicensePlateImagesList, detectedLicensePlateRegionList, licenses); continue; } RotatedRect box = CvInvoke.MinAreaRect(contour); 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 < 10.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 (hierachy[idx, 2] > 0) FindLicensePlate(contours, hierachy, hierachy[idx, 2], gray, canny, licensePlateImagesList, filteredLicensePlateImagesList, detectedLicensePlateRegionList, licenses); continue; } using (UMat tmp1 = new UMat()) using (UMat tmp2 = new UMat()) { PointF[] srcCorners = box.GetVertices(); PointF[] destCorners = new PointF[] { new PointF(0, box.Size.Height - 1), new PointF(0, 0), new PointF(box.Size.Width - 1, 0), new PointF(box.Size.Width - 1, box.Size.Height - 1)}; using (Mat rot = CvInvoke.GetAffineTransform(srcCorners, destCorners)) { CvInvoke.WarpAffine(gray, tmp1, rot, Size.Round(box.Size)); } //resize the license plate such that the front is ~ 10-12. This size of front results in better accuracy from tesseract Size approxSize = new Size(240, 180); double scale = Math.Min(approxSize.Width/box.Size.Width, approxSize.Height/box.Size.Height); Size newSize = new Size( (int)Math.Round(box.Size.Width*scale),(int) Math.Round(box.Size.Height*scale)); CvInvoke.Resize(tmp1, tmp2, newSize, 0, 0, Inter.Cubic); //removes some pixels from the edge int edgePixelSize = 2; Rectangle newRoi = new Rectangle(new Point(edgePixelSize, edgePixelSize), tmp2.Size - new Size(2*edgePixelSize, 2*edgePixelSize)); UMat plate = new UMat(tmp2, newRoi); UMat filteredPlate = FilterPlate(plate); Tesseract.Character[] words; StringBuilder strBuilder = new StringBuilder(); using (UMat tmp = filteredPlate.Clone()) { _ocr.Recognize(tmp); words = _ocr.GetCharacters(); if (words.Length == 0) continue; for (int i = 0; i < words.Length; i++) { strBuilder.Append(words[i].Text); } } licenses.Add(strBuilder.ToString()); licensePlateImagesList.Add(plate); filteredLicensePlateImagesList.Add(filteredPlate); detectedLicensePlateRegionList.Add(box); } } } } } /// <summary> /// Filter the license plate to remove noise /// </summary> /// <param name="plate">The license plate image</param> /// <returns>License plate image without the noise</returns> private static UMat FilterPlate(UMat plate) { UMat thresh = new UMat(); CvInvoke.Threshold(plate, thresh, 120, 255, ThresholdType.BinaryInv); //Image<Gray, Byte> thresh = plate.ThresholdBinaryInv(new Gray(120), new Gray(255)); Size plateSize = plate.Size; using (Mat plateMask = new Mat(plateSize.Height, plateSize.Width, DepthType.Cv8U, 1)) using (Mat plateCanny = new Mat()) using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint()) { plateMask.SetTo(new MCvScalar(255.0)); CvInvoke.Canny(plate, plateCanny, 100, 50); CvInvoke.FindContours(plateCanny, contours, null, RetrType.External, ChainApproxMethod.ChainApproxSimple); int count = contours.Size; for (int i = 1; i < count; i++) { using (VectorOfPoint contour = contours[i]) { Rectangle rect = CvInvoke.BoundingRectangle(contour); if (rect.Height > (plateSize.Height >> 1)) { rect.X -= 1; rect.Y -= 1; rect.Width += 2; rect.Height += 2; Rectangle roi = new Rectangle(Point.Empty, plate.Size); rect.Intersect(roi); CvInvoke.Rectangle(plateMask, rect, new MCvScalar(), -1); //plateMask.Draw(rect, new Gray(0.0), -1); } } } thresh.SetTo(new MCvScalar(), plateMask); } CvInvoke.Erode(thresh, thresh, null, new Point(-1, -1), 1, BorderType.Constant, CvInvoke.MorphologyDefaultBorderValue); CvInvoke.Dilate(thresh, thresh, null, new Point(-1, -1), 1, BorderType.Constant, CvInvoke.MorphologyDefaultBorderValue); return thresh; } protected override void DisposeObject() { _ocr.Dispose(); } }
5. 呼叫:
... private LicensePlateDetector _licensePlateDetector; private Capture _capture; private const string tessPath = @"C:\Craft\OpenCV\EmguTest\EmguCVLPR\tessdata"; private Timer _timer; public Form1() { InitializeComponent(); _licensePlateDetector = new LicensePlateDetector(tessPath); _capture = new Capture(); Detecting(); } public void Detecting() { var openFileDialog1 = new OpenFileDialog(); DialogResult result = openFileDialog1.ShowDialog(); if (result == DialogResult.OK) { Mat img; try { img = CvInvoke.Imread(openFileDialog1.FileName,LoadImageType.AnyColor); pictureBox1.ImageLocation = openFileDialog1.FileName; pictureBox1.Show(); } catch { MessageBox.Show(String.Format("Invalide File: {0}", openFileDialog1.FileName)); return; } UMat uImg = img.ToUMat(AccessType.ReadWrite); ProcessImage(uImg); } } private void ProcessImage(IInputOutputArray image) { try { Stopwatch watch = Stopwatch.StartNew(); // time the detection process List<IInputOutputArray> licensePlateImagesList = new List<IInputOutputArray>(); List<IInputOutputArray> filteredLicensePlateImagesList = new List<IInputOutputArray>(); List<RotatedRect> licenseBoxList = new List<RotatedRect>(); List<string> words = _licensePlateDetector.DetectLicensePlate( image, licensePlateImagesList, filteredLicensePlateImagesList, licenseBoxList); watch.Stop(); //stop the timer Point startPoint = new Point(10, 10); for (int i = 0; i < words.Count; i++) { Mat dest = new Mat(); CvInvoke.VConcat(licensePlateImagesList[i], filteredLicensePlateImagesList[i], dest); AddLabelAndImage( ref startPoint, String.Format("License: {0}", words[i]), dest, Stopwatch.GetTimestamp()); PointF[] verticesF = licenseBoxList[i].GetVertices(); Point[] vertices = Array.ConvertAll(verticesF, Point.Round); using (VectorOfPoint pts = new VectorOfPoint(vertices)) CvInvoke.Polylines(image, pts, true, new Bgr(Color.Red).MCvScalar, 2); } } finally { //_timer.Start(); } } private void AddLabelAndImage(ref Point startPoint, String labelText, IImage image, long totalSeconds) { pictureBox2.Image = image.Bitmap; label1.Text = labelText + "\r\n"+ label1.Text ; } ...