1. 程式人生 > >簡單驗證碼識別

簡單驗證碼識別


驗證碼識別主要包括兩部分:去除干擾和識別。其中最麻煩的是去除干擾。對於識別有現成的庫: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