關於中文的識別,效果比較好而且開源的應該就是Tesseract-OCR了,所以自己親身試用一下,分享到博客讓有同樣興趣的人少走彎路。
文中所用到的身份證圖片資源是百度找的,如有侵權可聯系我刪除。
一、准備工作
1、下載Tesseract-OCR引擎,注意要3.0以上才支持中文哦,按照提示安裝就行,Windows下:https://github.com/UB-Mannheim/tesseract/wiki。
2、下載chi_sim.traindata字庫。要有這個才能識別中文。下好后,放到Tesseract-OCR項目的tessdata文件夾里面。
3、下載jTessBoxEditor,這個是用來訓練字庫的。
以上的幾個在百度都能找到下載,就不詳細講了。
二、識別
1、進入cmd,進入到要識別的圖片的路徑下。
2、輸入命令
tesseract 圖片名稱 生成的結果文件的名稱 字庫
例如我的圖片識別就是:
tesseract test.jpg result -l chi_sim
識別完后會生成result.txt文件
當然啦效果不太理想。所以我們要訓練自己的字庫。
三、訓練
1、將圖片轉換成tif格式,用於后面生成box文件。可以通過畫圖,然后另存為tif即可。
更改圖片名字,這個是有要求的=。=
tif文面命名格式[lang].[fontname].exp[num].tif
lang是語言 fontname是字體
比如我們要訓練自定義字庫 mjorcen字體名normal
那么我們把圖片文件重命名 mjorcen.normal.exp0.jpg在轉tif。
2、生成box文件。
tesseract mjorcen.normal.exp0.jpg mjorcen.normal.exp0 -l chi_sim batch.nochop makebox
box文件和對應的tif一定要在相同的目錄下,不然后面打不開。
3、打開jTessBoxEditor矯正錯誤並訓練
打開train.bat
找到tif圖,打開,並校正。
4、訓練。
只要在命令行輸入命令即可。
tesseract mjorcen.normal.exp0.jpg mjorcen.normal.exp0 nobatch box.train
unicharset_extractor mjorcen.normal.exp0.box
在這我明明已經矯正好了,但是還是有1個字符不能識別出來,報的錯跟實際上完全沒有相關性,不知道是不是bug,到后面的結果就是“園”字沒有識別出來。
先不管,畢竟只有一個樣本。
新建一個font_properties文件
里面內容寫入 normal 0 0 0 0 0 表示默認普通字體
繼續敲命令
shapeclustering -F font_properties -U unicharset mjorcen.normal.exp0.tr mftraining -F font_properties -U unicharset -O unicharset mjorcen.normal.exp0.tr cntraining mjorcen.normal.exp0.tr
最后會生成五個文件,把目錄下的unicharset、inttemp、pffmtable、shapetable、normproto這五個文件前面都加上normal.
如圖:
命令行輸入,合並五個文件:
combine_tessdata normal.
得到訓練好的字庫。
四、測試
1、把 normal.traineddata 復制到Tesseract-OCR 安裝目錄下的tessdata文件夾中
2、識別命令:
tesseract mjorcen.normal.exp0.jpg mjorcen.normal.exp0 -l normal
3、效果
對比:
總結:肯定要自己訓練過后的字庫識別效果好,接下來要把整個項目弄進android,還要研究怎么將多個字庫合並成一個字庫,因為我不可能一次訓練完所有的圖片文字的。到時候有什么成果了再分享博文。希望大家可以點贊!謝謝。
更新:沒有錯誤的話命令行的提示應該是這樣的
c# 引用Tesseract,Tesseract是Google維護的一個OCR識別圖片文字的開源項目。而A .Net wrapper for the tesseract ocr Library則是可供.Net平台應用程序使用Tesseract的程序包,由Charles Weld在Github上創建的開源項目,下載量大且持續更新,
https://github.com/charlesw/tesseract通過nuget,如下:
以下是測試程序:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using Tesseract; namespace WindowsFormsApplication2 { public partial class Form1 : Form { const int CHARSIZE = 5; public Form1() { InitializeComponent(); string code; this.pictureBox1.Image = ValidateCodeUtils.CreateImage(CHARSIZE, out code); this.textBox1.Text = code; } private void 驗證碼_Click(object sender, EventArgs e) { string code; this.pictureBox1.Image = ValidateCodeUtils.CreateImage(CHARSIZE, out code); this.textBox1.Text = code; } private void StartOCR() { string txt = ""; //string defaultList = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; string defaultList = "2345689ABCDEFGHJKLMNPRSTWXY"; const string language = "eng"; //Nuget安裝的Tessract版本為3.20,tessdata的版本必須與其匹配,另外路徑最后必須以"\"或者"/"結尾 const string TessractData = @"D:\Program Files\Tesseract-OCR\tessdata\"; TesseractEngine test = new TesseractEngine(TessractData, language); test.SetVariable("tessedit_char_whitelist", defaultList); Bitmap tmpVal = (Bitmap)this.pictureBox1.Image; //灰度化,最新版本的Tesseract內部已經有了類似灰度化、二值化的封裝,彩色一樣可以識別,只是,驗證碼加上噪點或噪線識別的就亂七八糟了 var tmpImage = ToGray(tmpVal); this.pictureBox2.Image = tmpImage; Page tmpPage = test.Process(tmpImage, pageSegMode: test.DefaultPageSegMode); txt = tmpPage.GetText(); this.textBox2.Text = txt; } private void button1_Click(object sender, EventArgs e) { StartOCR(); } //圖像灰度化:http://www.cnblogs.com/gdjlc/archive/2013/03/05/2943801.html //將彩色圖像轉化成為灰度圖像的過程成為圖像的灰度化處理。 //彩色圖像中的每個像素的顏色有R、G、B三個分量決定,而每個分量有255中值可取, //這樣一個像素點可以有1600多萬(255*255*255)的顏色的變化范圍。 //而灰度圖像是R、G、B三個分量相同的一種特殊的彩色圖像,其一個像素點的變化范圍為255種, //所以在數字圖像處理種一般先將各種格式的圖像轉變成灰度圖像以使后續的圖像的計算量變得少一些。 //灰度圖像的描述與彩色圖像一樣仍然反映了整幅圖像的整體和局部的色度和亮度等級的分布和特征。 //圖像的灰度化處理可用兩種方法來實現。 //第一種方法使求出每個像素點的R、G、B三個分量的平均值,然后將這個平均值賦予給這個像素的三個分量。 //第二種方法是根據YUV的顏色空間中,Y的分量的物理意義是點的亮度,由該值反映亮度等級, //根據RGB和YUV顏色空間的變化關系可建立亮度Y與R、G、B三個顏色分量的對應:Y=0.3R+0.59G+0.11B,以這個亮度值表達圖像的灰度值。 /// <summary> /// 圖像灰度化 /// </summary> /// <param name="bmp"></param> /// <returns></returns> public static Bitmap ToGray(Bitmap bmp) { for (int i = 0; i < bmp.Width; i++) { for (int j = 0; j < bmp.Height; j++) { //獲取該點的像素的RGB的顏色 Color color = bmp.GetPixel(i, j); //利用公式計算灰度值 int gray = (int)(color.R * 0.3 + color.G * 0.59 + color.B * 0.11); Color newColor = Color.FromArgb(gray, gray, gray); bmp.SetPixel(i, j, newColor); } } return bmp; } //灰度反轉: //把每個像素點的R、G、B三個分量的值0的設為255,255的設為0。 /// <summary> /// 圖像灰度反轉 /// </summary> /// <param name="bmp"></param> /// <returns></returns> public static Bitmap GrayReverse(Bitmap bmp) { for (int i = 0; i < bmp.Width; i++) { for (int j = 0; j < bmp.Height; j++) { //獲取該點的像素的RGB的顏色 Color color = bmp.GetPixel(i, j); Color newColor = Color.FromArgb(255 - color.R, 255 - color.G, 255 - color.B); bmp.SetPixel(i, j, newColor); } } return bmp; } //灰度圖像二值化: //在進行了灰度化處理之后,圖像中的每個象素只有一個值,那就是象素的灰度值。它的大小決定了象素的亮暗程度。 //為了更加便利的開展下面的圖像處理操作,還需要對已經得到的灰度圖像做一個二值化處理。 //圖像的二值化就是把圖像中的象素根據一定的標准分化成兩種顏色。在系統中是根據象素的灰度值處理成黑白兩種顏色。 //和灰度化相似的,圖像的二值化也有很多成熟的算法。它可以采用自適應閥值法,也可以采用給定閥值法。 /// <summary> /// 圖像二值化1:取圖片的平均灰度作為閾值,低於該值的全都為0,高於該值的全都為255 /// </summary> /// <param name="bmp"></param> /// <returns></returns> public static Bitmap ConvertTo1Bpp1(Bitmap bmp) { int average = 0; for (int i = 0; i < bmp.Width; i++) { for (int j = 0; j < bmp.Height; j++) { Color color = bmp.GetPixel(i, j); average += color.B; } } average = (int)average / (bmp.Width * bmp.Height); for (int i = 0; i < bmp.Width; i++) { for (int j = 0; j < bmp.Height; j++) { //獲取該點的像素的RGB的顏色 Color color = bmp.GetPixel(i, j); int value = 255 - color.B; Color newColor = value > average ? Color.FromArgb(0, 0, 0) : Color.FromArgb(255, 255, 255); bmp.SetPixel(i, j, newColor); } } return bmp; } /// <summary> /// 圖像二值化2 /// </summary> /// <param name="img"></param> /// <returns></returns> public static Bitmap ConvertTo1Bpp2(Bitmap img) { int w = img.Width; int h = img.Height; Bitmap bmp = new Bitmap(w, h, PixelFormat.Format1bppIndexed); BitmapData data = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed); for (int y = 0; y < h; y++) { byte[] scan = new byte[(w + 7) / 8]; for (int x = 0; x < w; x++) { Color c = img.GetPixel(x, y); if (c.GetBrightness() >= 0.5) scan[x / 8] |= (byte)(0x80 >> (x % 8)); } Marshal.Copy(scan, 0, (IntPtr)((int)data.Scan0 + data.Stride * y), scan.Length); } bmp.UnlockBits(data); return bmp; } } public class ValidateCodeUtils { public static Bitmap CreateImage(int length, out string validateCode) { validateCode = string.Empty; //顏色列表,用於驗證碼、噪線、噪點 Color[] color = { Color.Black, Color.Purple, Color.Red, Color.Blue, Color.Brown, Color.Navy }; //字體列表,用於驗證碼 string[] font = { "Times New Roman", "MS Mincho", "Book Antiqua", "Gungsuh", "PMingLiU", "Impact" }; //驗證碼的字符集,去掉了一些容易混淆的字符 char[] character = { '2', '3', '4', '5', '6', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'W', 'X', 'Y' }; Random rnd = new Random(); //生成驗證碼字符串 for (int i = 0; i < length; i++) { validateCode += character[rnd.Next(character.Length)]; } Bitmap bmp = new Bitmap(length * 20 + 20, 40); Graphics g = Graphics.FromImage(bmp); g.Clear(Color.White); ////畫噪線 //for (int i = 0; i < 10; i++) //{ // int x1 = rnd.Next(20) * rnd.Next(5); // int y1 = rnd.Next(8) * rnd.Next(5); // int x2 = rnd.Next(20) * rnd.Next(5); // int y2 = rnd.Next(8) * rnd.Next(5); // Color clr = color[rnd.Next(color.Length)]; // g.DrawLine(new Pen(clr), x1, y1, x2, y2); //} //畫驗證碼字符串 for (int i = 0; i < validateCode.Length; i++) { string fnt = font[rnd.Next(font.Length)]; Font ft = new Font(fnt, 18); Color clr = color[rnd.Next(color.Length)]; g.DrawString(validateCode[i].ToString(), ft, new SolidBrush(clr), (float)i * 20 + 8, (float)8); } ////畫噪點 //for (int i = 0; i < 30; i++) //{ // int x = rnd.Next(bmp.Width); // int y = rnd.Next(bmp.Height); // Color clr = color[rnd.Next(color.Length)]; // bmp.SetPixel(x, y, clr); //} try { return bmp; } finally { //顯式釋放資源 g.Dispose(); } } } }
效果如下: