簡單的純數字圖像(如電話號碼、數字驗證碼)識別


又到歲末,大家都忙着撈年底最后一桶金,我也不例外,忙着采集數據,不過有時候需要付出一點點時間而已。


在本案例中,我遇到了一個純數字的電話號碼變成了圖片需要采集過來,在原網頁上以<img src="一個JSP文件地址加一串密碼" />的形式展現給我們,在采集的時候,有人建議我繞過去,直接采圖片算了,不過本着對品質的追求,還是覺得應該做到采集的同時轉化為文本。

我的思路是這樣的,先處理保存0-9及“-”的黑白圖片到本地磁盤,並分別取名為0.gif,1.gif....9.gif,-.gif,之后采集圖片流到內存中,處理成黑白圖片后,按長度等分切割,並與本地圖片循環比對。這種情況也僅適合於“純數字的簡單”圖片。請注意,在本案例中,沒有紋路識別,只有像素比對。

於是,試驗品開始了:
首先,得到遠端圖片的地址,並根據這個地址得到Response出來的圖片(注,這是一個流,並非一個真正的圖片文件,就像網站上的圖片驗證碼一樣。)。

在這里我用到了HttpWebRequest,但是發現直接代入圖片地址后GET到的是空白,於是加參數.Referer = "http://該網站的域名",好了,現在遠端給了我圖片的流。


本段偽代碼如下:

View Code
 1 HttpWebRequest objRequest = (HttpWebRequest)WebRequest.Create( " 圖片地址 ");
 2             objRequest.Timeout =  10000; // 設置超時10秒        
 3              objRequest.Referer =  " http://被采網站的域名/ ";             
 4             HttpWebResponse objResponse = (HttpWebResponse)objRequest.GetResponse();          
 5             System.IO.Stream resStream = objResponse.GetResponseStream(); 
 6             從以上代碼中,我得到這樣一副圖片:
 7              // 保存圖片的代碼也供上。
 8              // System.Drawing.Image i = System.Drawing.Image.FromStream(resStream);
 9              // i.Save(@"c:\x.gif ", System.Drawing.Imaging.ImageFormat.Gif);
10              // resStream.Close();
11              // i.Dispose(); 

 

第二步,圖片流得到了,接下來是處理這副圖片了。不過在這之前,我還有件重要的事情要做,因為是純數字的圖片,並且可能區號和電話之間有“-”符號,所以我必須在本地保存了這些圖片的黑白樣品,這個過程很簡單,就是用PHOTOSHOP去色,然后到調整亮度/對比度,各增加100,即可得到一張黑白的圖片,之后將其切分為10*20的小圖,分別取名為0.gif,1.gif...9.gif,-.gif一共11個圖片。

,之后,我的處理流程是:圖片流到內存====》變灰處理=====》加亮度,對比度=====》變黑白處理=====》切分並和本地這11張圖片比對

 

View Code
1                 Bitmap iGetPhoto =  new Bitmap(resStream);
2                  // 第一步 變灰度圖
3                  iGetPhoto = ToGray(iGetPhoto);
4                  // 第二步 增加亮度100
5                  iGetPhoto = KiLighten(iGetPhoto,  100);
6                  // 第三步增加對比度100
7                  iGetPhoto = KiContrast(iGetPhoto,  100);
8                  // 第四步 變黑白
9                  iGetPhoto = ToBlackWhite(iGetPhoto);

 

四個函數體:

View Code
  1          ///   <summary>
  2           ///  圖片變成灰度
  3           ///   </summary>
  4           ///   <param name="b"></param>
  5           ///   <returns></returns>
  6           public Bitmap ToGray(Bitmap b) {
  7              for ( int x =  0; x < b.Width; x++)
  8             {
  9                  for ( int y =  0; y < b.Height; y++)
 10                 {
 11                     Color c = b.GetPixel(x, y);
 12                      int luma = ( int)(c.R *  0.3 + c.G *  0.59 + c.B *  0.11); // 轉換灰度的算法
 13                      b.SetPixel(x, y, Color.FromArgb(luma, luma, luma));
 14                 }
 15             }return b;
 16         }
 17          ///   <summary>
 18           ///  圖像變成黑白
 19           ///   </summary>
 20           ///   <param name="b"></param>
 21           ///   <returns></returns>
 22           public Bitmap ToBlackWhite(Bitmap b) {
 23              for ( int x =  0; x < b.Width; x++)
 24             {
 25                  for ( int y =  0; y < b.Height; y++)
 26                 {
 27                     Color c = b.GetPixel(x, y);
 28                      if (c.R < ( byte) 255)
 29                     {
 30                         b.SetPixel(x, y, Color.FromArgb( 000));
 31                     }
 32                 }
 33             }return b;
 34         }
 35          ///   <summary>
 36           ///  圖像亮度調整
 37           ///   </summary>
 38           ///   <param name="b"></param>
 39           ///   <param name="degree"></param>
 40           ///   <returns></returns>
 41           public Bitmap KiLighten(Bitmap b,  int degree)
 42         {
 43 
 44              if (b ==  null)
 45             {
 46 
 47                  return  null;
 48 
 49             }
 50 
 51 
 52 
 53              if (degree < - 255) degree = - 255;
 54 
 55              if (degree >  255) degree =  255;
 56 
 57 
 58 
 59              try
 60             {
 61 
 62 
 63 
 64                  int width = b.Width;
 65 
 66                  int height = b.Height;
 67 
 68 
 69 
 70                  int pix =  0;
 71 
 72 
 73 
 74                 BitmapData data = b.LockBits( new Rectangle( 00, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
 75 
 76 
 77 
 78                  unsafe
 79                 {
 80 
 81                      byte* p = ( byte*)data.Scan0;
 82 
 83                      int offset = data.Stride - width *  3;
 84 
 85                      for ( int y =  0; y < height; y++)
 86                     {
 87 
 88                          for ( int x =  0; x < width; x++)
 89                         {
 90 
 91                              //  處理指定位置像素的亮度
 92 
 93                              for ( int i =  0; i <  3; i++)
 94                             {
 95 
 96                                 pix = p[i] + degree;
 97 
 98 
 99 
100                                  if (degree <  0) p[i] = ( byte)Math.Max( 0, pix);
101 
102                                  if (degree >  0) p[i] = ( byte)Math.Min( 255, pix);
103 
104 
105 
106                             }  //  i
107 
108                             p +=  3;
109 
110                         }  //  x
111 
112                         p += offset;
113 
114                     }  //  y
115 
116                 }
117 
118 
119 
120                 b.UnlockBits(data);
121 
122 
123 
124                  return b;
125 
126             }
127 
128              catch
129             {
130 
131                  return  null;
132 
133             }
134 
135 
136 
137         } 
138 
139         
140          ///   <summary>
141 
142           ///  圖像對比度調整
143 
144           ///   </summary>
145 
146           ///   <param name="b"> 原始圖 </param>
147 
148           ///   <param name="degree"> 對比度[-100, 100] </param>
149 
150           ///   <returns></returns>
151 
152          public Bitmap KiContrast(Bitmap b,  int degree)
153         {
154 
155              if (b ==  null)
156             {
157 
158                  return  null;
159 
160             }
161 
162 
163 
164              if (degree < - 100) degree = - 100;
165 
166              if (degree >  100) degree =  100;
167 
168 
169 
170              try
171             {
172 
173 
174 
175                  double pixel =  0;
176 
177                  double contrast = ( 100.0 + degree) /  100.0;
178 
179                 contrast *= contrast;
180 
181                  int width = b.Width;
182 
183                  int height = b.Height;
184 
185                 BitmapData data = b.LockBits( new Rectangle( 00, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
186 
187                  unsafe
188                 {
189 
190                      byte* p = ( byte*)data.Scan0;
191 
192                      int offset = data.Stride - width *  3;
193 
194                      for ( int y =  0; y < height; y++)
195                     {
196 
197                          for ( int x =  0; x < width; x++)
198                         {
199 
200                              //  處理指定位置像素的對比度
201 
202                              for ( int i =  0; i <  3; i++)
203                             {
204 
205                                 pixel = ((p[i] /  255.0 -  0.5) * contrast +  0.5) *  255;
206 
207                                  if (pixel <  0) pixel =  0;
208 
209                                  if (pixel >  255) pixel =  255;
210 
211                                 p[i] = ( byte)pixel;
212 
213                             }  //  i
214 
215                             p +=  3;
216 
217                         }  //  x
218 
219                         p += offset;
220 
221                     }  //  y
222                  }
223                 b.UnlockBits(data);
224                  return b;            
225             } catch
226             {
227                  return  null;
228             }
229         }

第三步,所有的准備工作已經完成了,開始比對!內存中有了一副黑白的數字圖,它的尺寸是140*20,並且左邊空出來7像素,右側待定,具體看電話號碼有幾位,可能是3像素,也可能是13像素;本地磁盤中有了0-9.gif,它們的尺寸是10*20,現在要做的就是比對:比對代碼如下:

 

View Code
 1                  // 讀取物理磁盤的文件到內存,注意這個.ToServerPath()是擴展方法,它的原型是HttpContext.Server.Mappath("xxx")
 2                  Bitmap[] numColl = {
 3                      new Bitmap(( " /Temp/0.gif ").ToServerPath()),
 4                      new Bitmap(( " /Temp/1.gif ").ToServerPath()),
 5                      new Bitmap(( " /Temp/2.gif ").ToServerPath()),
 6                      new Bitmap(( " /Temp/3.gif ").ToServerPath()),
 7                      new Bitmap(( " /Temp/4.gif ").ToServerPath()),
 8                      new Bitmap(( " /Temp/5.gif ").ToServerPath()),
 9                      new Bitmap(( " /Temp/6.gif ").ToServerPath()),
10                      new Bitmap(( " /Temp/7.gif ").ToServerPath()),
11                      new Bitmap(( " /Temp/8.gif ").ToServerPath()),
12                      new Bitmap(( " /Temp/9.gif ").ToServerPath()),
13                      new Bitmap(( " /Temp/-.gif ").ToServerPath())
14                 };

 

 

 

View Code
 1          ///   <summary>
 2           ///  比較原圖和每個小樣圖,並給出數字結果
 3           ///   </summary>
 4           ///   <param name="iGetPhoto"></param>
 5           ///   <param name="numColl"></param>
 6           ///   <returns></returns>
 7           public  string ComparePic(Bitmap iGetPhoto /* 原圖 */, Bitmap[] numColl /* 小圖樣圖集 */) {
 8              int numCount =  13;
 9              string result =  string.Empty;
10              for ( int i =  0; i < numCount; i++)
11             {
12                  int x = i *  10 +  7; // 原始圖的開始取像素位置
13                  Bitmap perBmp =  new Bitmap( 1020);
14                 Graphics gPhoto = Graphics.FromImage(perBmp);
15                 gPhoto.Clear(Color.White);
16                 gPhoto.DrawImage(iGetPhoto /* 原圖 */00, /* 目標位置 */  new Rectangle( new Point(x,  0),  new Size( 1020)) /* 源位置 */, GraphicsUnit.Pixel);
17                  for ( int j =  0; j <  11; j++) // 這是數字樣圖的集合循環
18                  {
19                      bool isTrue =  true; // 接下來循環小圖的每一個像素,與大圖中裁出的小圖作比較,只要一個像素不對,就OVER
20                       for ( int n =  0; n <  20; n++)
21                     {
22                          for ( int m =  0; m <  10; m++)
23                         {
24                             Color point1 = perBmp.GetPixel(m, n);
25                             Color point2 = numColl[j].GetPixel(m, n);
26                              if (point1.ToArgb() != point2.ToArgb())
27                             {
28                                 isTrue =  false;
29                             }
30                         }
31                     }
32                      if (isTrue)
33                     {
34                         result += j ==  10 ?  " - " : j.ToString();
35                          break;
36                     }
37                 }
38                 perBmp.Dispose();
39                 gPhoto.Dispose();
40             }
41              return result;
42         }

 

 

最后,把零散的調用封裝成調用的入口函數:

View Code
 1          // 入口函數
 2           public  string GetPicTel( string url)
 3         {
 4             HttpWebRequest objRequest = (HttpWebRequest)WebRequest.Create(url);
 5             objRequest.Timeout =  10000; // 設置尾5秒        
 6              objRequest.Referer =  " http://你要偷圖片的網站域名 "
 7              try
 8             {
 9                 HttpWebResponse objResponse = (HttpWebResponse)objRequest.GetResponse();  
10                  // System.IO.Stream resStream = objResponse.GetResponseStream();
11                   // System.Drawing.Image i = System.Drawing.Image.FromStream(resStream);
12                   // i.Save(@"c:\x.gif ", System.Drawing.Imaging.ImageFormat.Gif);
13                   // resStream.Close();
14                   // i.Dispose(); 
15 
16                 System.IO.Stream resStream = objResponse.GetResponseStream();                
17                 Bitmap iGetPhoto =  new Bitmap(resStream);
18                  // 第一步 變灰度圖
19                  iGetPhoto = ToGray(iGetPhoto);
20                  // 第二步 增加亮度100
21                  iGetPhoto = KiLighten(iGetPhoto,  100);
22                  // 第三步增加對比度100
23                  iGetPhoto = KiContrast(iGetPhoto,  100);
24                  // 第四步 變黑白
25                  iGetPhoto = ToBlackWhite(iGetPhoto);
26 
27                
28 
29                  // 測試圖片的結果
30                   // iGetPhoto.Save(@"c:\x.gif ", System.Drawing.Imaging.ImageFormat.Gif);
31                   // resStream.Close();
32                   // return string.Empty;
33                  Bitmap[] numColl = {
34                      new Bitmap(( " /Temp/0.gif ").ToServerPath()),
35                      new Bitmap(( " /Temp/1.gif ").ToServerPath()),
36                      new Bitmap(( " /Temp/2.gif ").ToServerPath()),
37                      new Bitmap(( " /Temp/3.gif ").ToServerPath()),
38                      new Bitmap(( " /Temp/4.gif ").ToServerPath()),
39                      new Bitmap(( " /Temp/5.gif ").ToServerPath()),
40                      new Bitmap(( " /Temp/6.gif ").ToServerPath()),
41                      new Bitmap(( " /Temp/7.gif ").ToServerPath()),
42                      new Bitmap(( " /Temp/8.gif ").ToServerPath()),
43                      new Bitmap(( " /Temp/9.gif ").ToServerPath()),
44                      new Bitmap(( " /Temp/-.gif ").ToServerPath())
45                 };
46                  return ComparePic(iGetPhoto, numColl);
47             
48             
49             }
50              catch (Exception ex)
51             {
52                  return  string.Empty;
53             }
54         }

 另外,要說明的是,有些驗證碼(不帶扭曲),有傾斜或上下波動的也可以用這種方法搞定,只是需要再多動一點點腦筋.但是帶扭曲效果的驗證碼就是非常專業的事情了,不過我們只用來做采集,不是爆破專家,這種方式應該基本滿足應用了.

希望本文可以拋磚引玉,幫你采集到你需要的數據,當然,盡可能地支持別人的版權!也正是因為版權,恕我無法給出原始圖片地址.

 

本文來自http://corecainiao.cnblogs.com/如需轉載請標明出處。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM