簡單驗證碼識別
阿新 • • 發佈:2019-01-09
驗證碼識別主要包括兩部分:去除干擾和識別。其中最麻煩的是去除干擾。對於識別有現成的庫:tesseract。在進行驗證碼識別之前,首先需要得到驗證碼資料,如果從網上下載是在太麻煩,就寫了一個生成驗證碼的程式,用來生成各種隨機驗證碼。
一、生成驗證碼資料集
1)驗證碼的隨機性,包括字串本身的隨機性,字型以及字型大小的隨機性以及字型旋轉和各種隨機干擾線。
2)簡單的程式碼:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace GenSecurityCode { public partial class SecurityCodeGen : Form { public const int DEFAULT_FONT_SIZE = 20; private int iCodeLen = 6; private int iFontSize = DEFAULT_FONT_SIZE; private int iInterferLines = 6; private int iCodeNums = 20; public string batchSavePath = string.Empty; public SecurityCodeGen() { InitializeComponent(); } private void SecurityCodeGen_Load(object sender, EventArgs e) { checkNum.Checked = true; checkUpperCase.Checked = false; checkLowerCase.Checked = false; textCodeLength.Text = iCodeLen.ToString(); txtCodeNums.Text = iCodeNums.ToString(); } /*Generate Security Code String */ private string GenCodeString() { string codeStr = String.Empty; char code; System.Random random = new Random(); int iRandCode; if (checkNum.Checked) { for (int i = 0; i < iCodeLen; i++) { iRandCode = random.Next(); code = (char)('0' + (char)(iRandCode % 10)); codeStr += code.ToString(); } } return codeStr; } private System.Drawing.Bitmap GenCodeImage(string sCodeStr) { char[] chars = sCodeStr.ToCharArray(); //define random color Color[] colorArray = { Color.Black, Color.Red, Color.DarkBlue, Color.Green, Color.Orange, Color.Brown, Color.DarkCyan, Color.Purple }; //define random font string[] fontArray = { "Verdana", "Microsoft Sans Serif", "Comic Sans MS", "Arial", "宋體" }; //define random transform angle int iTransAngleRange = 0; //define image System.Drawing.Bitmap image = new System.Drawing.Bitmap((int)Math.Ceiling((sCodeStr.Length * iFontSize* 1.0)), (int)Math.Ceiling(iFontSize * 2.0)); Graphics g = Graphics.FromImage(image); //Gen Backgroud Image; System.Random random = new Random(); g.Clear(Color.White); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; //draw interferior lines for (int i = 0; i < iInterferLines; i++) { int x1 = random.Next(image.Width); int x2 = random.Next(image.Width); int y1 = random.Next(image.Height); int y2 = random.Next(image.Height); int colorIndex = random.Next(colorArray.Length); g.DrawLine(new Pen(colorArray[colorIndex]), x1, y1, x2, y2); } StringFormat format = new StringFormat(StringFormatFlags.NoClip); format.Alignment = StringAlignment.Center; format.LineAlignment = StringAlignment.Center; //draw security code string for (int i = 0; i < chars.Length; i++) { int fontIndex = 1;//random.Next(5);//no random font used first int colorIndex = random.Next(colorArray.Length); int transAngleIndex = 0;// no transform first; Font font = new System.Drawing.Font(fontArray[fontIndex], iFontSize, System.Drawing.FontStyle.Bold); Point dot = new Point(iFontSize/8, iFontSize/8); Brush brush = new System.Drawing.SolidBrush(colorArray[colorIndex]); float angle = random.Next(-transAngleIndex, transAngleIndex); g.TranslateTransform(dot.X, dot.Y); g.RotateTransform(angle); g.DrawString(chars[i].ToString(), font, brush, 1, 1); //MessageBox.Show(chars[i].ToString()); g.RotateTransform(-angle); g.TranslateTransform((int)Math.Ceiling(iFontSize*0.75), -dot.Y); //g.DrawString(sCodeStr.ToString(), font, brush, 1, 1); } //draw interferior point for (int i = 0; i < 150; i++) { int x = random.Next(image.Width); int y = random.Next(image.Height); image.SetPixel(x, y, Color.FromArgb(random.Next())); } //draw border //g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1); return image; } private void btnGen_Click(object sender, EventArgs e) { string codeStr = GenCodeString(); System.Drawing.Bitmap image = GenCodeImage(codeStr); txtCodeStr.Text = codeStr; this.picShowCtrl.Width = image.Width; this.picShowCtrl.Height = image.Height; this.picShowCtrl.BackgroundImage = image; image.Save(codeStr + ".jpg"); } private void btnBatchGen_Click(object sender, EventArgs e) { if (batchSavePath.Length == 0) { FolderBrowserDialog folderDlg = new FolderBrowserDialog(); folderDlg.ShowDialog(); batchSavePath = folderDlg.SelectedPath; if (batchSavePath.Length == 0) { MessageBox.Show("You didn't select path!"); } } iCodeNums = int.Parse(txtCodeNums.Text); iCodeLen = int.Parse(textCodeLength.Text); txtCodeStr.Text = string.Empty; for (int i = 0; i < iCodeNums; i++) { string codeStr = GenCodeString(); System.Drawing.Bitmap image = GenCodeImage(codeStr); if (txtCodeStr.Text.Length != 0) //txtCodeStr.Text += Environment.NewLine +codeStr; txtCodeStr.Text += "," + codeStr; else txtCodeStr.Text = codeStr; if(batchSavePath.Length==0) image.Save(codeStr + ".jpg"); else image.Save(batchSavePath+"\\"+codeStr + ".jpg"); System.Threading.Thread.Sleep(50); } } } }
二、去除干擾
Mat PreProcess(Mat &img) { Mat grayImg,binaryImg,erodeImg,dilateImg,maskedImg; cvtColor(img, grayImg, CV_RGB2GRAY); Mat mask(img.rows, img.cols, CV_8UC1,Scalar(0)); /*OSTU threshold*/ threshold(grayImg, binaryImg, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV); imwrite("binary.jpg", binaryImg); /*erode to remove noise,erode and dilate function only work on white pixel*/ erode(binaryImg, erodeImg, Mat(2, 2, CV_8U), Point(-1, -1), 1); imwrite("erode.jpg", erodeImg); /*dilate to link contour*/ dilate(erodeImg, dilateImg, Mat(3, 3, CV_8U), Point(-1, -1), 1); imwrite("dilate.jpg", dilateImg); dilateImg.copyTo(grayImg); /*find contour*/ vector<vector<Point>> contours; findContours(dilateImg, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); imwrite("dilate1.jpg", grayImg); /*remove interferior*/ vector<vector<Point>>::iterator it; Point2f center; float radius; Rect rc; for (it = contours.begin(); it != contours.end();){ minEnclosingCircle(*it, center, radius); if (center.y > (img.rows / 4) && center.y < img.rows * 3 / 4){
<span style="white-space:pre"> </span>/*you can add more condition to remove more interferior*/ rc = boundingRect(*it); rectangle(mask, rc, CV_RGB(255,255,255), -1); it++; } else{/*please note how to erase one item in vector*/ it = contours.erase(it); } } imwrite("mask.jpg", mask); //copy(dilateImg, maskedImg, mask); grayImg.copyTo(maskedImg, mask); erode(maskedImg, erodeImg, Mat(2, 2, CV_8U), Point(-1, -1), 1); bitwise_not(erodeImg, erodeImg); imwrite("masked.jpg", erodeImg); return erodeImg; }
三、字元識別
字元識別最麻煩其實是tesseract的庫的編譯,使用其實很簡單。
#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>
/*snippet code for recognition */
tesseract::TessBaseAPI *api = new tesseract::TessBaseAPI();
// Initialize tesseract-ocr with English, without specifying tessdata path
if (api->Init(NULL, "eng")) {
fprintf(stderr, "Could not initialize tesseract.\n");
exit(1);
}
api->SetPageSegMode(tesseract::PSM_SINGLE_BLOCK);
api->SetImage((uchar*)img.data, img.cols, img.rows, 1, img.cols);
char* out = api->GetUTF8Text();
std::cout << "recognition:" << out << std::endl;
tesseract 編譯 請參照這裡進行編譯
https://github.com/charlesw/tesseract-vs2012