0

我设法使 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 来获取它。

日产车牌识别

但是,我的程序无法识别任何字符添加全部。我得到的最接近的是这张照片。但这不是我想要识别的图片

宝马车牌识别

是由于图像大小还是我需要做进一步的图像预处理。提前致谢

4

0 回答 0