利用OpenCvSharp與Tesseract套件,進行車牌辨識

今天心血來潮隨意寫寫的玩具程式碼,路過的人參考參考就好,真正要上戰場運作得要不斷的研究與精化,影像辨識有很多適用場景與範圍需要討論,也有可能需要引入AI等訓練模型來優化。這篇文章重點是採用離線的方式進行識別,並且站在OpenCvSharp與Tesseract這兩個巨人的肩膀上。

使用nuget引入套件

  1. OpenCvSharp4
  1. Tesseract

撰寫車牌辨識程式碼

有關於影像辨識的程式碼都列入在下方的,沒啥好說的,大致上就是灰階、設定閥值、框選想要處理的矩形區塊、OCR文字識別這樣而已。

public static void Main()
{
  var stopwatch = new Stopwatch();
  stopwatch.Start();
  Mat image = Cv2.ImRead($@"D:\test.jpg", ImreadModes.Color);
  var licensePlateNumber = RecognizeLicensePlate(image).Trim();
  stopwatch.Stop();
  Console.WriteLine($"識別輸出:{licensePlateNumber}");
  Console.WriteLine($"耗費時間:{stopwatch.Elapsed.TotalMilliseconds}ms");
  Console.Read();
}

private static string RecognizeLicensePlate(Mat image)
{
  Mat gray = new Mat();
  Cv2.CvtColor(image, gray, ColorConversionCodes.BGR2GRAY);
  Mat threshold = new Mat();
  Cv2.AdaptiveThreshold(gray, threshold, 255, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, 11, 2);
  Cv2.FindContours(threshold, out var contours, out var hierarchyIndices, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple);

  var licensePlateRegions = new List<OpenCvSharp.Rect>();
  for (int i = 0; i < contours.Length; i++)
  {
    var rect = Cv2.BoundingRect(contours[i]);
    float aspectRatio = (float)rect.Width / rect.Height;
    if (aspectRatio > 2 && aspectRatio < 6.0 && rect.Width > 60 && rect.Height > 20)
    {
      licensePlateRegions.Add(rect);
    }
  }

  string licensePlateNumber = string.Empty;
  foreach (var rect in licensePlateRegions)
  {
    using (var engine = new TesseractEngine(@"./tessdata", "eng", EngineMode.LstmOnly))
    { //消除「Estimating resolution as」輸出
      engine.SetVariable("user_defined_dpi", 96);
      //Treat the image as a single text line.
      engine.SetVariable("tessedit_pageseg_mode", 7);
      using (var page = engine.Process(Pix.LoadFromMemory(new Mat(image, rect).ToBytes())))
      {
        string extractedText = page.GetText().Trim();
        licensePlateNumber += $"{(extractedText.Length != 0 ? extractedText : string.Empty)}\n";
      }
    }
  }
  return licensePlateNumber;
}

執行畫面

運行起來後,跑去網路隨便抓一張苦主的車牌號碼進行辨識(車牌請恕我馬賽克),果然可行。但再去抓了幾張比較模糊的照片,或是沒那麼正矩形的車牌,其辨識率就不是很高,但總之大致上的模樣已經有出來了。

後記

以上程式碼與大家分享,這是我花了一些小時間寫出來的概念性驗證程式碼,有驗證出概念我就完事了,所以別寫信來要求提高辨識率,我的本業與興趣並非在此。但如果你可以依據上面的程式碼調教出更好的參數,歡迎來信分享給我,我會把你的程式碼與大名更新回這個頁面。(取之於網路用之於網路)

我心目中最佳化的車牌辨識系統應該是以YOLO這種卷積神經網路(Convolutional Neural Network,CNN)為出發點的AI識別系統,先識別物件找出車輛,再針對車輛去拿車牌,這樣速度才會又快又準。這種全畫面去找邊緣化找矩形的做法其實很土炮(車牌拍的歪斜一點就毀了),我認為不是現代電腦程式碼應該撰寫或處理的方向。

CSharp OpenCvSharp Tesseract 影像辨識 車牌辨識 車號辨識