C# OpenCV6 -车牌识别

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/csharp25/article/details/84679703

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 ;
        }

...

猜你喜欢

转载自blog.csdn.net/csharp25/article/details/84679703
今日推荐