青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆 - 298  文章 - 377  trackbacks - 0
<2016年6月>
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789

常用鏈接

留言簿(34)

隨筆分類

隨筆檔案

文章檔案

相冊

收藏夾

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

菜鳥學習筆記供新手學習參考,一個有著很多限制的小程序,大神路過多多指點。


  不廢話,正題如下:


  bmp驗證碼識別的流程大概分為:

    1、獲取bmp圖片中識別需要用到的數據,包括像素高度、像素寬度以及位圖數據。

    2、對獲取的數據進行必要的處理,包括灰度化、二值化、去噪等。

    3、對處理后的數據(一個二值化的數組)進行掃描,分割出各個字符(即確定多對數組下標)。

    4、建立字符模板庫,將分割出的字符與模板一一對比,找出對應的字符。


  下面具體講解:

  1、獲取數據。

    bmp文件結構與讀取遍地都是,細節不贅述。

    需要注意:1)簡單識別用到的也就是biWidth(19-22字節),biHeight(23-26字節),以及位圖數據(>54字節)。

                       2)位圖數據是倒著存放,讀取或處理的時候要進行處理。

                               位圖數據每行會自動填充字節為4的倍數,讀取或處理時最好跳過相應數目的字節。

                        3)每個像素有3個分量RGB,對應3個字節。

                       4)關于調色板:由于現在基本都是24位真彩圖,所以這個基本可以不考慮,所以才是讀取54字節以后的為位圖數據。

                                                       調色板個人理解:     類似超市寄存物品,每個格子就是一種顏色,通過你的編號(即位圖數據,注

                                                                                           意這里每個像素不是3字節,而是因多少位的圖片而異),拿到你想要的顏色。

  1. //      提供方法:  
  2. //              1、取得圖片信息。  
  3. //              2、取得圖片數據。  
  4.   
  5.   
  6.   
  7. class readBmp  
  8. {  
  9. public:  
  10.     bool            getWidth(long &);                   //得到位圖的寬  
  11.     bool            getHeight(long &);                  //得到位圖的高  
  12.     bool            getBit(int &);                      //得到位圖位數  
  13.     bool            getData(bit_t *&);              //讀取文件的顏色信息,即像素部分,即rgb信息  
  14.   
  15.     readBmp(const char *path);  
  16.     ~readBmp();  
  17.   
  18. private:  
  19.     int             bitWidth;                 
  20.     int             bitHeight;                
  21.     int             bitBit;                   
  22.     bool            bitReadSuccess;         //記錄構造函數里bmp文件是否成功讀取  
  23.     void            distroy();  
  24.     bit_t          *tmpData;                //從文件讀出的數據地址  
  25.     bool            isInforExisted(void);   //判斷bmp文件是否成功讀取  
  26. };  
  27.   
  28. /* 
  29. 在構造函數里讀取所需的文件信息,包括像素高度、寬度、和位圖數據 
  30. 自己申請的空間在析構函數釋放 
  31. */  
  32.   
  33. readBmp::readBmp(const char *path)  
  34. {  
  35.     ifstream  bmpFile(path, ios::in | ios::binary);         //創建一個讀取文件流對象, 不自動生成不存在文件,二進制方式打開  
  36.     bitReadSuccess = bmpFile.good();                        //判斷文件是否成功打開  
  37.     if(true == bitReadSuccess)  
  38.     {  
  39.         bmpFile.seekg(18, ios::beg);                        //bmp文件結構中WIDTH 和 HEIGHT為long類型,在19-26字節  
  40.         bmpFile.read((char *)(&bitWidth), 4);               //由于read方法參數類型限制,要將第一個參數如左處理  
  41.   
  42.         //64位系統g++ long 為8字節,不能用sizeof(long)獲取字節數,否則文件指針偏移量過大,讀取過多  
  43.   
  44.         bmpFile.read((char *)(&bitHeight), 4);        
  45.         bmpFile.seekg(2, ios::cur);  
  46.         bmpFile.read((char *)(&bitBit), sizeof (bitBit));  
  47.           
  48.         if (bitBit != 24)                                   //暫時只考慮24位圖片的讀取,2、4、8位的可以以后擴充  
  49.         {  
  50.             cout << "非24位圖片,請選擇合適的圖片重試!" << endl;  
  51.         }  
  52.         else  
  53.         {  
  54.             long count = 0;  
  55.             bmpFile.seekg(22, ios::cur);  
  56.             tmpData = new bit_t[bitWidth * bitHeight * 3];  
  57.             long    skipWidth = (4 - bitWidth*3%4)%4;           //計算每行讀取時要skip的字節數  
  58.             for(int i = bitHeight - 1; i >= 0; i--)  
  59.             {  
  60.                 for( int j = 0; j < bitWidth * 3; j++)  
  61.                     bmpFile.get(tmpData[count++]);  
  62.                 bmpFile.seekg(skipWidth, ios::cur);                     //跳過計算過的字節數  
  63.             }  
  64.         }  
  65.                   
  66.         bmpFile.close();  
  67.     }  
  68. }  
  69.   
  70.   
  71. bool    readBmp::getWidth(long &width)                                      //獲取像素寬度  
  72. {  
  73.     if (isInforExisted() == true)  
  74.         width = bitWidth;  
  75.     else  
  76.         return false;  
  77.     return true;  
  78. }  
  79.   
  80. bool    readBmp::getHeight(long &height)                                        //獲取像素高度  
  81. {  
  82.     if (isInforExisted() == true)  
  83.         height = bitHeight;  
  84.     else  
  85.         return false;  
  86.     return true;  
  87. }  
  88.   
  89. bool    readBmp::getBit(int &bit)                                           //獲取位圖位數  
  90. {  
  91.     if (isInforExisted() == true)  
  92.         bit = bitBit;  
  93.     else  
  94.         return false;  
  95.     return true;  
  96. }  
  97.   
  98. bool    readBmp::isInforExisted()                                           //判斷文件信息是否讀入  
  99. {  
  100.     if (false == bitReadSuccess)  
  101.         cout << "圖片文件信息讀取失敗,請重新讀取后重試!" << endl;  
  102.     return bitReadSuccess;  
  103. }  
  104.   
  105. bool    readBmp::getData(bit_t *&data)  
  106. {  
  107.     if (isInforExisted() == true)  
  108.         data = tmpData;  
  109.     else  
  110.         return false;  
  111.     return true;  
  112. }  
  113.   
  114. readBmp::~readBmp()  
  115. {  
  116.     delete []tmpData;  
  117. }  


  2、數據處理。

    位圖數據讀取完成應該得到一個一維數組,總共biWidth*biHeight*3個字節。(本人用char類型存儲)

    1)灰度化。

           像素信息RGB 3個分量一般是不同的,共同表達一中顏色信息。當R=G=B時表達的是一種灰色。因此將圖像灰度化只需要將

     3個分量的值變為相等的即可,可以采用加權法、最大值法等,具體可百度“灰度化”。

      注意:采用何種方法灰度化對后面的二值化影響很大,可分別采用測試效果,由于本人處理的圖片比較特殊,用的最大值法。

                   前面得到的一維數組在這一步可以轉化為一個二維數組  arr[biHeight][biWidth]  (3個分量可合并為1個)。

     2)二值化。

           其實就是根據算法或經驗從位圖數據中得到一個值(網稱閾值),遍歷灰度化后得到的二維數組,利用這個值將數組二值化。

      我用的是全域迭代法,效果一般,求閾值可自選算法或自選值。

     3)去噪。

            也就是去除圖片中的干擾點、雜點。我用的是很屌絲的與周圍的點比較異同的方法(多調用幾遍效果也還不錯)。大神自尋算法。

  1. //一些圖片的處理方法  
  2. //創建此對象時同時進行灰度化  
  3.   
  4. class sortBmp  
  5. {  
  6. public:  
  7.     sortBmp(int width, int height, char *indata);  
  8.     ~sortBmp();  
  9.     int             getThreshold();                             //計算閾值  
  10.     bool            binarize();                                 //二值化  
  11.     bool            show();                                     //輸出數據信息  
  12.     bool            noiseSort();                                //去噪  
  13.     int           **getData();  
  14.   
  15. private:  
  16.     bool            isGrey;  
  17.     bool            isBinary;  
  18.     int             bitWidth;  
  19.     int             bitHeight;  
  20.     int           **bmpData;  
  21.     int             maxGreyValue, minGreyValue;  
  22.     int             Threshold;  
  23.     int             greyValueExisted[256], greyValueCount[256], greyValueExistedCount;  
  24.     bool            isGreyValueExisted(int);  
  25.     bool            addGreyValueCount(int);  
  26.     bool            addGreyValueMember(int);  
  27.     bool            greyValueExistedSort();  
  28.     double          sum_data(intintint *, int *);  
  29.     double          sum_data(intintint *);  
  30.     bool            deleteNoisePoint();  
  31. };    
  32.   
  33. sortBmp::sortBmp(const int width, const int height, char *indata):bitWidth(width), bitHeight(height)  
  34. {  
  35.     int i, j, k, key;  
  36.     int tmp[3];  
  37. //動態申請一個二維數組  
  38.     bmpData = new int*[height];  
  39.   
  40.     for(k = 0; k < height; k++)  
  41.     {   
  42.         bmpData[k] = new int[width];  
  43.     }  
  44. //key記錄以為數組的下標  
  45.     key = -1;  
  46. //注意第一維的下標,以此實現數據的倒置  
  47.     for (i = height - 1; i >= 0; i--)  
  48.         for (j = 0; j < width; j++)  
  49.         {  
  50. //          bmpData[i][j] = ((indata[++key] & 0xFF)*0.3 + (indata[++key] & 0xFF)*0.59 + (indata[++key] & 0xFF)*0.11);  //jia quan  
  51. //            bmpData[i][j] = ((indata[++key] & 0xFF) + (indata[++key] & 0xFF) + (indata[++key] & 0xFF))/3;               //ping jun  
  52.            tmp[0] = indata[++key] & 0xFF;  
  53.            tmp[1] = indata[++key] & 0xFF;  
  54.            tmp[2] = indata[++key] & 0xFF;                       //與0xff與運算char轉換為int  
  55.            bmpData[i][j] = (tmp[0]>tmp[1]?tmp[0]:tmp[1])>tmp[2]?(tmp[0]>tmp[1]?tmp[0]:tmp[1]):tmp[2];  
  56.         }  
  57.     isGrey = true;  
  58.     isBinary = false;  
  59. //測試輸出  
  60. //  cout << endl << endl;  
  61. //  for(int i = 0; i < width*height*3; i++)  
  62. //      cout << (indata[i] & 0xFF) << '\t';  
  63. //  cout << endl;  
  64.  }  
  65.   
  66. sortBmp::~sortBmp()  
  67. {   
  68.     for (int i = 0; i < bitHeight; i++)  
  69.         delete []bmpData[i];  
  70.     delete []bmpData;  
  71. //  delete []bmpData;  
  72. //  cout << "對象析構。" << endl;  
  73. }  
  74. int sortBmp::getThreshold()             //獲取閾值的算法。 本質是漸進到一個合適的值  
  75. {  
  76.     int threshold[2], tmpThreshold; //用來保存初始閾值、緩存閾值和最終的閾值  
  77.     maxGreyValue = **bmpData;  
  78.     minGreyValue = **bmpData;  
  79.     for (int i = 0; i < 256; i++)  
  80.         greyValueCount[i] = 0;  
  81.     greyValueExistedCount = 0;  
  82.     for (int i = 0; i < bitHeight; i++)  
  83.         for (int j = 0; j < bitWidth; j++)  
  84.         {  
  85. //獲取最大最小灰度值  
  86. //測試語句  
  87. //          cout << bmpData[i][j] << ' ' ;  
  88.             if (maxGreyValue < bmpData[i][j])  
  89.                 maxGreyValue = bmpData[i][j];  
  90.             if (minGreyValue > bmpData[i][j])  
  91.                 minGreyValue = bmpData[i][j];  
  92.         //如果灰度值已錄入,就增加統計的數目,如果未錄入,就將其錄入       
  93.             if (isGreyValueExisted(bmpData[i][j]) == true)  
  94.                 addGreyValueCount(bmpData[i][j]);  
  95.             else  
  96.                 addGreyValueMember(bmpData[i][j]);      //此函數要在greyValueExisted里添加數據,修改greyValueCount和greyValueExistedCount的值  
  97.         }  
  98.       
  99.     greyValueExistedSort();  
  100.     //準備數據完畢。  
  101.     threshold[0] = 0;  
  102.     threshold[1] = (maxGreyValue + minGreyValue)/2;  
  103.     while(threshold[1] != threshold[0])  
  104.     {  
  105.         tmpThreshold =   
  106.             0.4 * (  
  107.             sum_data(greyValueExisted[0], threshold[1], greyValueExisted, greyValueCount)  
  108.             /sum_data(greyValueExisted[0], threshold[1], greyValueCount)  
  109.             +  
  110.             sum_data(threshold[1]+1, greyValueExisted[greyValueExistedCount-1], greyValueExisted, greyValueCount)  
  111.             /sum_data(threshold[1]+1, greyValueExisted[greyValueExistedCount-1], greyValueCount)  
  112.             );  
  113.     threshold[0] = threshold[1];  
  114.     threshold[1] = tmpThreshold;  
  115.     }  
  116.   
  117.     Threshold = threshold[1];  
  118.     return threshold[1];  
  119. }   
  120.   
  121. double sortBmp::sum_data(int start_value, int end_value, int *value_data, int *count_data)  
  122. {  
  123.     int sum_4_arg = 0;  
  124.     int i = 0;  
  125.     while (greyValueExisted[i] < start_value)  
  126.         i++;  
  127.         while (greyValueExisted[i] <= end_value && i < greyValueExistedCount)  
  128.     {  
  129.         sum_4_arg += value_data[i]*count_data[i];  
  130.             i++;  
  131.      }  
  132.     return sum_4_arg;  
  133. }   
  134.   
  135. double sortBmp::sum_data(int start_value, int end_value, int *count_data)  
  136. {   
  137.     int sum_3_arg = 0;  
  138.     int i = 0;  
  139.     while (greyValueExisted[i] < start_value)  
  140.         i++;  
  141.     while (greyValueExisted[i] <= end_value && i < greyValueExistedCount)  
  142.     {  
  143.         sum_3_arg += count_data[i];  
  144.             i++;  
  145.      }  
  146.     return sum_3_arg;  
  147. }   
  148.   
  149. bool sortBmp::isGreyValueExisted(int data)  
  150. {  
  151.     if (greyValueExistedCount == 0)  
  152.         return false;  
  153.     for (int i = 0; i < greyValueExistedCount; i++)  
  154.         if (data == greyValueExisted[i])  
  155.             return true;  
  156.     return false;  
  157. }   
  158.   
  159. bool    sortBmp::addGreyValueMember(int data)  
  160. {   
  161.     greyValueExisted[greyValueExistedCount] = data;  
  162.     greyValueCount[greyValueExistedCount]++;  
  163.     greyValueExistedCount++;  
  164.     return true;  
  165. }  
  166.   
  167. bool    sortBmp::addGreyValueCount(int data)  
  168. {  
  169.     for (int i = 0; i < greyValueExistedCount; i++)  
  170.         if (greyValueExisted[i] == data)  
  171.             greyValueCount[i]++;  
  172.     return true;  
  173. }   
  174.   
  175. bool    sortBmp::greyValueExistedSort()  
  176. {  
  177.     int tmp_existed, tmp_count;  
  178.   
  179.     for (int i = 0; i < greyValueExistedCount - 1; i++)  
  180.         for (int j = i+1; j < greyValueExistedCount; j++)  
  181.         {  
  182.             if (greyValueExisted[i] > greyValueExisted[j])  
  183.             {  
  184.                 tmp_existed = greyValueExisted[i];  
  185.                 greyValueExisted[i] = greyValueExisted[j];  
  186.                 greyValueExisted[j] = tmp_existed;  
  187.                 tmp_count = greyValueCount[i];  
  188.                 greyValueCount[i] = greyValueCount[j];  
  189.                 greyValueCount[j] = tmp_count;  
  190.             }   
  191.         }  
  192.     return true;  
  193. }  
  194. //以上都是為算法服務 -。-  
  195. bool    sortBmp::binarize()  
  196. {  
  197.     for (int i = 0; i < bitHeight; i++)  
  198.         for (int j = 0; j < bitWidth; j++)  
  199.             if (bmpData[i][j] > Threshold)  
  200.                 bmpData[i][j] = 255;  
  201.             else  
  202.                 bmpData[i][j] = 0;  
  203.     return true;  
  204. }  
  205.   
  206. bool    sortBmp::show()  
  207. {  
  208.     for(int i = 0; i < bitWidth; i++)  
  209.         cout << '-';  
  210.     cout << endl;  
  211.     for (int i = 0; i < bitHeight; i++)  
  212.     {  
  213.         for (int j = 0; j < bitWidth; j++)  
  214.             if (bmpData[i][j] == 0)  
  215.                 cout << '*';  
  216.             else  
  217.                 cout << ' ';  
  218.         cout << '|' << endl;         
  219.     }  
  220.     for(int i = 0; i < bitWidth; i++)  
  221.         cout << '-';  
  222.     cout << endl;  
  223. /*    for (int i = 0; i < bitHeight; i++) 
  224.     { 
  225.         for (int j = bitWidth+1; j < bitWidth; j++) 
  226.             if (bmpData[i][j] == 0) 
  227.                 cout << '*'; 
  228.             else 
  229.                 cout << ' ';   
  230.     } 
  231.  */   
  232.   
  233. }  
  234.   
  235. bool    sortBmp::deleteNoisePoint()                 //在這里去噪,道理簡單,寫起來真難受  
  236. {  
  237.     for (int i = 1; i < bitHeight-1; i++)  
  238.         for (int j = 1; j < bitWidth-1; j++)  
  239.             if (((bmpData[i-1][j-1] != bmpData[i][j]) + (bmpData[i-1][j] != bmpData[i][j]) + (bmpData[i-1][j+1] != bmpData[i][j]) + (bmpData[i][j-1] != bmpData[i][j]) + (bmpData[i][j+1] != bmpData[i][j]) + (bmpData[i+1][j-1] != bmpData[i][j]) + (bmpData[i+1][j] != bmpData[i][j]) + (bmpData[i+1][j+1] != bmpData[i][j])) >= 7)//用6會損失很多數據點,選擇后續處理3個噪點在一起的情況   
  240.                 bmpData[i][j] = 255;//~bmpData[i][j];  
  241.    //處理4個角   
  242. /*  if ((bmpData[0][0] != bmpData[0][1]) + (bmpData[0][0] != bmpData[1][0]) + (bmpData[0][0] != bmpData[1][1]) >= 2) 
  243.         bmpData[0][0] = ~bmpData[0][0]; 
  244.     if ((bmpData[0][bitWidth-1] != bmpData[0][bitWidth-2]) + (bmpData[0][bitWidth-1] != bmpData[1][bitWidth-2]) + (bmpData[0][bitWidth-1] != bmpData[1][bitWidth-1]) >= 2) 
  245.         bmpData[0][bitWidth-1] = ~bmpData[0][0]; 
  246.     if ((bmpData[bitHeight-1][0] != bmpData[bitHeight-2][0]) + (bmpData[bitHeight-1][0] != bmpData[bitHeight-2][1]) + (bmpData[bitHeight-1][0] != bmpData[bitHeight-1][1]) >= 2) 
  247.         bmpData[0][0] = ~bmpData[0][0]; 
  248.     if ((bmpData[bitHeight-1][bitWidth-1] != bmpData[bitHeight-2][bitWidth-1]) + (bmpData[bitHeight-1][bitWidth-1] != bmpData[bitHeight-2][bitWidth-2]) + (bmpData[bitHeight-1][bitWidth-1] != bmpData[bitHeight-1][bitWidth-2]) >= 2) 
  249.         bmpData[0][0] = ~bmpData[0][0]; 
  250.         */  
  251.     bmpData[0][0] = bmpData[0][bitWidth-1] = bmpData[bitHeight-1][0] = bmpData[bitHeight-1][bitWidth-1] = 255;  
  252.     //處理除角的邊界  
  253.     for (int i = 1; i < bitWidth-2; i++)  
  254.         if ((bmpData[0][i] != bmpData[0][i-1]) + (bmpData[0][i] != bmpData[0][i+1]) + (bmpData[0][i] != bmpData[1][i]) + (bmpData[0][i] != bmpData[1][i-1]) + (bmpData[0][i] != bmpData[1][i+1]) >= 4)  
  255.             bmpData[0][i] = 255;//~bmpData[0][i];  
  256.     for (int i = 1; i < bitHeight-2; i++)  
  257.         if ((bmpData[i][0] != bmpData[i-1][0]) + (bmpData[i][0] != bmpData[i+1][0]) + (bmpData[i][0] != bmpData[i][1]) + (bmpData[i][0] != bmpData[i-1][1]) + (bmpData[i][0] != bmpData[i+1][1]) >= 4)  
  258.             bmpData[i][0] = 255;//~bmpData[i][0];  
  259.   
  260.     for (int i = 1; i < bitWidth-2; i++)  
  261.         if ((bmpData[bitHeight-1][i] != bmpData[bitHeight-1][i-1]) + (bmpData[bitHeight-1][i] != bmpData[bitHeight-1][i+1]) + (bmpData[bitHeight-1][i] != bmpData[bitHeight-2][i]) + (bmpData[bitHeight-1][i] != bmpData[bitHeight-2][i-1]) + (bmpData[bitHeight-1][i] != bmpData[bitHeight-2][i+1]) >= 4)  
  262.             bmpData[bitHeight-1][i] = 255;//~bmpData[bitHeight-1][i];  
  263.   
  264.    for (int i = 1; i < bitHeight-2; i++)  
  265.         if ((bmpData[i][bitWidth-1] != bmpData[i-1][bitWidth-1]) + (bmpData[i][bitWidth-1] != bmpData[i+1][bitWidth-1]) + (bmpData[i][bitWidth-1] != bmpData[i][bitWidth-2]) + (bmpData[i][bitWidth-1] != bmpData[i-1][bitWidth-2]) + (bmpData[i][bitWidth-1] != bmpData[i+1][bitWidth-2]) >= 4)  
  266.             bmpData[i][bitWidth-1] = 255;//~bmpData[i][bitWidth-1];  
  267.     return true;  
  268. }  
  269.   
  270. bool    sortBmp::noiseSort()   //多調用即便去噪更徹底  
  271. {  
  272.     deleteNoisePoint();  
  273.     deleteNoisePoint();  
  274.     deleteNoisePoint();  
  275.     return true;  
  276. }  
  277. int**   sortBmp::getData()  
  278. {  
  279.     return bmpData;  
  280. }  


  3、字符分割。

    其實從數據處理開始可選行就很強了,流程是一樣的,方法卻有很多,可以根據自己的能力和想要的效果自己選取合適的算法。

     由于我要處理的圖片只有4個字符且字符都是分開的,所以我也就遍歷這個二維數組,得到8對邊界, 每個字符兩對。


  4、模板匹配。

    這個算法可選行也很強,由于要求不高我還是用的最簡單的方法。

    將所有字符高、寬中的最大值最為二維數組的兩個范圍,這個限定大小的二維數組加上一個char類型數據就是一個模板。

    比如所有的字符最寬的1個寬為12,最的一個為14,那么模板就是arr[14][12]、在加上其對應字符。

    遇到一個字符先判斷模板文件中是否存在,存在就輸出對應信息。

    如果不存在,將字符存入這個數組,然后將這個二維數組和對應的字符(自己輸入)存入文件。  

  1. class recognizeBmp  
  2. {   
  3. public:  
  4.     recognizeBmp(intintint **);  
  5.     void        showCharacter();  
  6.     void        showResult();  
  7. private:  
  8.     int         wide_range[8];  
  9.     int         height_range[8];  
  10.     int         bitHeight;  
  11.     int         bitWidth;  
  12.     int       **bmpData;  
  13.     void        getCharacter(void);  
  14.     void        getRanges(void);  
  15.     void        get_inform(void);  
  16.     bool        recognize(int (*)[16], char &, intint);  
  17.     void        addCharacter(int (*)[16], char);  
  18.     bool        compare_char_arr(int (*)[16], int (*)[16], intint);  
  19. };  
  20.   
  21.   
  22.     recognizeBmp::recognizeBmp(int height, int width, int**tmpData):bitWidth(width), bitHeight(height), bmpData(tmpData)  
  23.     {  
  24.         getRanges();  
  25.     }   
  26.   
  27. void    recognizeBmp::getRanges(void)               //取得邊界范圍  
  28. {  
  29.   
  30.     int      i          = 0;  
  31.     int      h          = 0;  
  32.     int      w          = 0;  
  33.     long     sum        = 255;  
  34.     long     tmp_sum    = 255;  
  35.   
  36.     for (w = 0; w < bitWidth; w++)               //處理4對左右邊界  
  37.     {  
  38.         tmp_sum = sum;  
  39.         sum = 255;  
  40.         h = 0;  
  41.         while (h < bitHeight)  
  42.         {  
  43.             sum &= bmpData[h++][w];  
  44.         }  
  45.         if (sum != tmp_sum)  
  46.             wide_range[i++] = w;  
  47.     }  
  48.     sum = 255;  
  49.     for (h = 0; h < bitHeight; h++)  
  50.     {  
  51.         sum &= bmpData[h][bitWidth-1];  
  52.     }  
  53.     if (sum == 0)  
  54.         wide_range[7] = bitWidth-1;  
  55.   
  56.   
  57.     for (i = 1; i <= 7; i+=2)                    //處理4對上下邊界  
  58.         if (wide_range[i] != bitWidth-1)  
  59.             wide_range[i]--;    
  60.     for (int count = 0; count < 4; count++)  
  61.     {   
  62.         tmp_sum = 255;  
  63.         sum = 255;  
  64.         for (h = 0; h < bitHeight; h++)  
  65.         {  
  66.             tmp_sum = sum;                      //從上找上邊界  
  67.             sum = 255;  
  68.             for (w = wide_range[2*count]; w < wide_range[2*count+1]; w++)  
  69.             {  
  70.                 sum &= bmpData[h][w];  
  71.              }  
  72.              if (tmp_sum != sum)  
  73.              {  
  74.                 height_range[2*count] = h;  
  75.                 break;  
  76.              }  
  77.         }  
  78.         tmp_sum = 255;  
  79.         sum = 255;  
  80.         for (h = bitHeight-1; h >= 0; h--)  
  81.         {  
  82.             tmp_sum = sum;                  //從下找下邊界  
  83.             sum = 255;  
  84.             for (w = wide_range[2*count]; w < wide_range[2*count+1]; w++)  
  85.             {  
  86.                 sum &= bmpData[h][w];  
  87.             }  
  88.             if (tmp_sum != sum)  
  89.             {  
  90.               height_range[2*count+1] = h;  
  91.               break;  
  92.             }  
  93.         }  
  94.     }              
  95.   
  96.     for (int count = 0; count < 4; count++)                  //假如有字符邊界在最底部的話  
  97.     {  
  98.         sum = 255;  
  99.         for (i = wide_range[2*count]; i < wide_range[2*count+1]; i++)  
  100.         {  
  101.             sum &= bmpData[bitHeight-1][w];  
  102.         }  
  103.         if (sum != 255)  
  104.           height_range[2*count +1] = bitHeight-1;  
  105.     }   
  106. }   
  107.   
  108. void    recognizeBmp::showCharacter(void)                   //輸出字符  
  109. {  
  110.     for (int count = 0; count < 4; count++)  
  111.     {  
  112.         for (int i = height_range[count*2]; i <= height_range[count*2+1]; i++)  
  113.         {  
  114.             for (int j = wide_range[count*2]; j <= wide_range[count*2+1]; j++)  
  115.             {  
  116.                 if (bmpData[i][j] == 255)  
  117.                     cout << ' ';  
  118.                 else  
  119.                     cout << '*';  
  120.             }  
  121.             cout << endl;  
  122.         }  
  123.         cout << endl;  
  124.     }  
  125. }  
  126.   
  127. /* 
  128. void    recognizeBmp::normalize(void) 
  129.     int sum = 0; 
  130.     int tmp_char[10][10] = {0}; 
  131.  
  132.     for (int c = 0; c < 4; c++) 
  133.     { 
  134.         for (int i = 0; i < 10; i++) 
  135.             for (int j = 0; j < 10; j++) 
  136.             { 
  137.                 sum = 0; 
  138.                 for (int h = height_range[2*c]+i*(height_range[2*c+1]-height_range[2*c]+1)/10; 
  139.                      h < height_range[2*c]+(i+1)*(height_range[2*c+1]-height_range[2*c]+1)/10  
  140.                       && h <= height_range[2*c+1];  h++)  
  141.                     for (int w = wide_range[2*c]+j*(wide_range[2*c+1]-wide_range[2*c]+1)/10; 
  142.                         w < wide_range[2*c]+(j+1)*(wide_range[2*c+1]-wide_range[2*c]+1)/10 
  143.                         && w <= wide_range[2*c+1]; w++) 
  144.                         if (bmpData[h][w] == 0) 
  145.                             sum++; 
  146. //              if (sum > (i*(height_range[2*c+1]-height_range[2*c]+1)/10 +1 )*(j*(wide_range[2*c+1]-wide_range[2*c]+1)/10 +1 )*0.4 && sum != 0) 
  147.                 if (sum >= 2) 
  148.                     tmp_char[i][j] = 1; 
  149.             } 
  150.         //測試語句 
  151.         for (int i = 0; i < 10; i++) 
  152.         { 
  153.             for (int j = 0; j < 10; j++) 
  154.                 cout << tmp_char[i][j] << ' '; 
  155.             cout << endl; 
  156.         } 
  157.         cout << endl; 
  158.     } 
  159. */      //歸一化失敗,另尋它法  
  160.   
  161. void    recognizeBmp::get_inform(void)  
  162. {  
  163.     int     tmp_char[16][16]    = {0};              //字符最大有16  
  164.     int     i                   = 0;  
  165.     int     j                   = 0;  
  166.     int     width               = 0;  
  167.     int     height              = 0;  
  168.     char    character;  
  169.     for (int c = 0; c < 4; c++)                      //4個字符4次識別或者錄入  
  170.     {  
  171.         height = height_range[2*c+1] - height_range[2*c] + 1;  
  172.         width  =   wide_range[2*c+1] -   wide_range[2*c] + 1;  
  173.         i = 0;  
  174.         for (int h = height_range[2*c]; h <= height_range[2*c+1]; h++)  //只輸入字符范圍的數據  
  175.         {  
  176.             j = 0;  
  177.             for (int w = wide_range[2*c]; w <= wide_range[2*c+1]; w++)  
  178.             {  
  179.                 tmp_char[i][j] = bmpData[h][w];  
  180.                 j++;  
  181.             }  
  182.             i++;  
  183.         }  
  184.         if (true == recognize(tmp_char, character, height, width))      //檢驗是否可識別  
  185.             cout << "第" << c+1 << "個字符是:" << character << endl;  
  186.         else  
  187.         {  
  188.             cout << "字符信息不存在,請添加以便下次使用" << endl << "字符:" << endl;  //不可識別的話就錄入  
  189.             for (int h = height_range[2*c]; h <= height_range[2*c+1]; h++)           //輸出讓用戶判斷是什么字符  
  190.             {  
  191.                 for (int w = wide_range[2*c]; w <= wide_range[2*c+1]; w++)  
  192.                     if (bmpData[h][w] == 0)  
  193.                         cout << '*';  
  194.                     else  
  195.                         cout << ' ';  
  196.                     cout << endl;  
  197.             }  
  198.             cout << endl << "第" << c+1 << "個字符是:" << endl;  
  199.             cin >> character;  
  200.             addCharacter(tmp_char, character);  
  201.         }  
  202.     }  
  203. }  
  204.       
  205.   
  206. void    recognizeBmp::showResult(void)  
  207. {  
  208.     get_inform();  
  209.     cout << endl << endl;  
  210. }  
  211.   
  212. bool    recognizeBmp::recognize(int (*tmp_char)[16], char &character, int height, int width)  
  213. {  
  214.     int     test_char[16][16] = {0};  
  215.     if ((double)height/width >= 1.5)             //對特殊字符的處理,提高準確率,不過貌似沒什么效果,不知到問題在哪  
  216.     {  
  217.         width *= 2;  
  218.     }  
  219.     ifstream infile("../data/characters", ios::binary | ios::in);  
  220.     if (infile.eof())  
  221.         return false;  
  222.     while (!infile.eof())  
  223.     {  
  224.         infile.read((char *)test_char, 16*16*4);  
  225.         character = infile.get();  
  226.         if (true == compare_char_arr(tmp_char, test_char, height, width))  //字符與模板比較  
  227.             return true;  
  228.     }  
  229.     return false;  
  230.     infile.close();  
  231. }  
  232.   
  233. void    recognizeBmp::addCharacter(int (*tmp_char)[16], char character)     //存入新的模板  
  234. {  
  235.     ofstream outfile("../data/characters", ios::binary | ios::app);  
  236.     outfile.write((char *)tmp_char, 16*16*4);  
  237.     outfile.put(character);  
  238.     outfile.close();  
  239. }  
  240.   
  241. bool    recognizeBmp::compare_char_arr(int (*tmp_char)[16], int (*test_char)[16], int height, int width)  
  242. {  
  243.     int count = 0;  
  244.     for (int i = 0; i < height; i++)  
  245.         for (int j = 0; j < width; j++)  
  246.             if (tmp_char[i][j] == test_char[i][j])  
  247.                 count++;  
  248.     if(count >= height*width*7/8)  //  7/8是多次測試得到的合適的值  
  249.         return true;  
  250.     else  
  251.         return false;  
  252. }  



  基本就這么多了,只是給毫無頭緒的朋友們提供一個思路,具體細節可以自己實現。

  這個程序其實是未完成的,所以有很多缺陷和限制。

  本來的思路:

                1、先掃描一定數量的圖片,啟動學習模塊,將未識別的字符存入模板文件,最終得到一個模板文件。

                 2、識別圖片, 啟動識別模塊,無法識別直接跳過識別下一個圖片(假如以刷票為目的,保證一定成功率就行了)

posted on 2013-04-07 22:58 聶文龍 閱讀(4262) 評論(2)  編輯 收藏 引用

FeedBack:
# re: 簡單的bmp驗證碼識別 (c++) 2013-04-07 23:01 聶文龍
簡單驗證碼是指驗證碼圖片里的字符,固定不變,或者變化很小的驗證碼,比如:字符上會有一些感擾點感擾線等情況,或者只有少數幾種字體變化和字體大小變化。

這里給出源代碼都是非常簡單的源代碼,學過《C程序設計》的都可以看懂,不會超出書本范圍,而且不使用指針,這樣VB,Delphi,JAVA 等語言都可以照此寫出相應的源代碼。

驗證碼識別可以分為三個大的步驟:預處理,分割,識別。預處理又可細分為讀取圖片,二值化,去感擾點,去感擾線等等步驟。

讀取圖片很簡單就是把要識別的驗證碼圖片讀取到我們的程序里,網絡上常見的驗證碼圖片格式有BMP,JPG,GIF,PNG,其中BMP的圖片格式種類很多,但都不復雜,24位BMP格式最簡單,除去54字節的文件頭,只需要順序讀取BMP文件的字節信息就可以了,256色的也就是8位的BMP格式也很常見,256色BMP圖片在文件頭后多一個索引表,后面也是順序存儲圖片信息的。JPG,GIF,PNG的格式都復雜一點,不過網上都有這些公司或組織發布的source code。

下面以24位BMP格式的源代碼進行說明:

#define W 40 //定義驗證碼圖片的寬度
#define H 20 //定義驗證碼圖片的寬度
#define N 4 //定義字符位數,一般是4位
#define BMP_filename "c:\\test.bmp"
int x,y,i,s,temp;
int YZM[W][H]={0};
int YZM_red[W][H]={0};
int YZM_green[W][H]={0};
int YZM_blue[W][H]={0};
int rec[W]={0};
int lf[N],rt[N],up[N],dw[N];
char result[N];

FILE *fp=fopen(BMP_filename,"rb");//以二進制只讀方式打開BMP文件
for(i=0;i<54;i++)temp=fgetc(fp);//跳過54個字節的文件頭數據
for(y=H-1;y>=0;y--)//BMP圖片數據是倒序存儲的,據說是為了下載顯示方便,呵呵
{
for(x=0;x<W;x++){YZM_red[x][y]=fgetc(fp);YZM_green[x][y]=fgetc(fp);YZM_blue[x][y]=fgetc(fp);}
//依次讀取每個像素的RGB值
}
fclose(fp);//關閉文件

好的,通過以上幾個語句就可以把圖片信息讀取到程序的數組里了,然后對其進行二值化,去感擾等工作。
二值化就是把剛才讀取的RGB信息,轉換成01數據,0表示空白背景,1表示字符。

for(x=0;x<W;x++)for(y=0;y<H;y++)
{
if(YZM_red[x][y]*0.3+YZM_green[x][y]*0.6+YZM_blue[x][y]*0.1<200)YZM[x][y]=1;else YZM[x][y]=0;
//red*0.3+green*0.6+blue*0.1 是計算亮度的公式
}
//如果圖片里有感擾點,可以用下面的語句去除。
for(x=1;x<W-1;x++)for(y=1;y<H-1;y++)
{
if(YZM[x][y]==1 && YZM[x-1][y-1]==0 && YZM[x][y-1]==0 && YZM[x+1][y-1]==0 && YZM[x-1][y]==0 && YZM[x+1][y]==0 && YZM[x-1][y+1]==0 && YZM[x][y+1]==0 && YZM[x+1][y+1]==0)YZM[x][y]=0;
//如果一個點的值是1,而且它的周圍8個點的值都是0,那么這個點就是感擾點
}

經過這些預處理工作就可以得到一個二進制數組數據了。我們以一組普通的驗證碼圖片為例,



for(y=0;y<H;y++) { for(x=0;x<W;x++) printf(" %c",YZM[x][y]+'0'); printf("\n"); }

我們可以用上面的輸出語句進行輸出,輸出語句在最終的程序中可以刪除或者注釋掉。
輸出結果如下:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 1 1 1 1 1 1 1 0 0 0 0
0 0 1 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 1 1 0 0 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0
0 0 1 1 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 1 1 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0
0 0 1 1 0 1 1 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 0 1 1 1 0 0 0 0 0
0 0 1 1 1 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 1 1 0 1 1 1 0 0 0 1 1 1 0 0 1 1 0 0 0 0
0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0
0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 1 1 0 0 0
0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 1 1 0 0 0 0 1 1 0 0 0
0 0 0 1 1 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 1 1 0 0 0 1 1 0 0 1 1 0 0 0 0
0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

for(x=0;x<W;x++)
{ s=0;
for(y=0;y<H;y++)s=s+YZM[x][y];
if(s>0)rec[x]=1;else rec[x]=0;//rec記錄數組的斷連情況
}
for(i=0,x=1;x<W-1;x++)if(rec[x-1]==0 && rec[x]==1 && rec[x+1]==1){i++;lf[i]=x;}//計算每個字符的左邊界
for(i=0,x=1;x<W-1;x++)if(rec[x-1]==1 && rec[x]==1 && rec[x+1]==0){i++;rt[i]=x;}//計算每個字符的右邊界
for(i=1;i<=N;i++)for(x=0;x<W;x++)for(y=0;y<H;y++) if(x>=lf[i] && x<=rt[i] && YZM[x][y]==1)YZM[x][y]=i;
for(y=H-1;y>=0;y--)for(x=0;x<W;x++)for(i=1;i<=N;i++)if(YZM[x][y]==i)up[i]=y;//計算每個字符的上邊界

經過上面的分割語句,我們就完成了分割工作,我們可以用輸出語句進行輸出,輸出結果如下:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 1 0 0 0 0 0 2 2 0 0 0 0 0 0 3 3 3 3 0 0 0 4 4 4 4 4 4 4 0 0 0 0
0 0 1 1 0 0 0 0 0 0 0 0 0 2 2 2 0 0 0 0 0 3 3 0 0 3 3 0 0 4 4 0 0 0 0 0 0 0 0 0
0 0 1 1 0 0 0 0 0 0 0 0 2 2 2 2 0 0 0 0 3 3 0 0 0 0 3 0 0 4 4 0 0 0 0 0 0 0 0 0
0 0 1 1 0 1 1 1 0 0 0 0 0 0 2 2 0 0 0 0 3 3 0 0 0 0 0 0 0 4 4 0 4 4 4 0 0 0 0 0
0 0 1 1 1 0 0 1 1 0 0 0 0 0 2 2 0 0 0 0 3 3 0 3 3 3 0 0 0 4 4 4 0 0 4 4 0 0 0 0
0 0 0 0 0 0 0 0 1 1 0 0 0 0 2 2 0 0 0 0 3 3 3 0 0 3 3 0 0 0 0 0 0 0 0 4 4 0 0 0
0 0 0 0 0 0 0 0 1 1 0 0 0 0 2 2 0 0 0 0 3 3 0 0 0 0 3 3 0 0 0 0 0 0 0 4 4 0 0 0
0 0 1 1 0 0 0 0 1 1 0 0 0 0 2 2 0 0 0 0 3 3 0 0 0 0 3 3 0 4 4 0 0 0 0 4 4 0 0 0
0 0 0 1 1 0 0 1 1 0 0 0 0 0 2 2 0 0 0 0 0 3 3 0 0 3 3 0 0 0 4 4 0 0 4 4 0 0 0 0
0 0 0 0 1 1 1 1 0 0 0 0 2 2 2 2 2 2 0 0 0 0 3 3 3 3 0 0 0 0 0 4 4 4 4 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0


最后我們就可以進行識別工作,識別的方法有很多,可以進行逐點的精確識別,也可以進行模糊識別等等,甚至也可以不進行去感擾和分割工作直接進行識別(主要用于粘連的驗證碼),這都要根據具體情況來具體分析用什么方法最有效,簡單驗證碼的識別則沒有那么復雜,用很多方法都可以識別,這里使用的是從每個字符的左上邊界開始,精確判斷5行3列15個點來進行識別。

for(i=1;i<=N;i++)
{
if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==0 && YZM[lf[i]+2][up[i]+0]==0 &&
YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==0 && YZM[lf[i]+2][up[i]+1]==i &&
YZM[lf[i]+0][up[i]+2]==0 && YZM[lf[i]+1][up[i]+2]==i && YZM[lf[i]+2][up[i]+2]==i &&
YZM[lf[i]+0][up[i]+3]==i && YZM[lf[i]+1][up[i]+3]==i && YZM[lf[i]+2][up[i]+3]==0 &&
YZM[lf[i]+0][up[i]+4]==i && YZM[lf[i]+1][up[i]+4]==i && YZM[lf[i]+2][up[i]+4]==0 )result[i-1]='0';
if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==0 && YZM[lf[i]+2][up[i]+0]==i &&
YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==i && YZM[lf[i]+2][up[i]+1]==i &&
YZM[lf[i]+0][up[i]+2]==i && YZM[lf[i]+1][up[i]+2]==i && YZM[lf[i]+2][up[i]+2]==i &&
YZM[lf[i]+0][up[i]+3]==0 && YZM[lf[i]+1][up[i]+3]==0 && YZM[lf[i]+2][up[i]+3]==i &&
YZM[lf[i]+0][up[i]+4]==0 && YZM[lf[i]+1][up[i]+4]==0 && YZM[lf[i]+2][up[i]+4]==i )result[i-1]='1';
if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==0 && YZM[lf[i]+2][up[i]+0]==i &&
YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==i && YZM[lf[i]+2][up[i]+1]==i &&
YZM[lf[i]+0][up[i]+2]==i && YZM[lf[i]+1][up[i]+2]==i && YZM[lf[i]+2][up[i]+2]==0 &&
YZM[lf[i]+0][up[i]+3]==0 && YZM[lf[i]+1][up[i]+3]==0 && YZM[lf[i]+2][up[i]+3]==0 &&
YZM[lf[i]+0][up[i]+4]==0 && YZM[lf[i]+1][up[i]+4]==0 && YZM[lf[i]+2][up[i]+4]==0 )result[i-1]='2';
if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==i && YZM[lf[i]+2][up[i]+0]==i &&
YZM[lf[i]+0][up[i]+1]==i && YZM[lf[i]+1][up[i]+1]==i && YZM[lf[i]+2][up[i]+1]==0 &&
YZM[lf[i]+0][up[i]+2]==0 && YZM[lf[i]+1][up[i]+2]==0 && YZM[lf[i]+2][up[i]+2]==0 &&
YZM[lf[i]+0][up[i]+3]==0 && YZM[lf[i]+1][up[i]+3]==0 && YZM[lf[i]+2][up[i]+3]==0 &&
YZM[lf[i]+0][up[i]+4]==0 && YZM[lf[i]+1][up[i]+4]==0 && YZM[lf[i]+2][up[i]+4]==0 )result[i-1]='3';
if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==0 && YZM[lf[i]+2][up[i]+0]==0 &&
YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==0 && YZM[lf[i]+2][up[i]+1]==0 &&
YZM[lf[i]+0][up[i]+2]==0 && YZM[lf[i]+1][up[i]+2]==0 && YZM[lf[i]+2][up[i]+2]==0 &&
YZM[lf[i]+0][up[i]+3]==0 && YZM[lf[i]+1][up[i]+3]==0 && YZM[lf[i]+2][up[i]+3]==i &&
YZM[lf[i]+0][up[i]+4]==0 && YZM[lf[i]+1][up[i]+4]==i && YZM[lf[i]+2][up[i]+4]==i )result[i-1]='4';
if( YZM[lf[i]+0][up[i]+0]==i && YZM[lf[i]+1][up[i]+0]==i && YZM[lf[i]+2][up[i]+0]==i &&
YZM[lf[i]+0][up[i]+1]==i && YZM[lf[i]+1][up[i]+1]==i && YZM[lf[i]+2][up[i]+1]==0 &&
YZM[lf[i]+0][up[i]+2]==i && YZM[lf[i]+1][up[i]+2]==i && YZM[lf[i]+2][up[i]+2]==0 &&
YZM[lf[i]+0][up[i]+3]==i && YZM[lf[i]+1][up[i]+3]==i && YZM[lf[i]+2][up[i]+3]==0 &&
YZM[lf[i]+0][up[i]+4]==i && YZM[lf[i]+1][up[i]+4]==i && YZM[lf[i]+2][up[i]+4]==i )result[i-1]='5';
if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==0 && YZM[lf[i]+2][up[i]+0]==i &&
YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==i && YZM[lf[i]+2][up[i]+1]==i &&
YZM[lf[i]+0][up[i]+2]==i && YZM[lf[i]+1][up[i]+2]==i && YZM[lf[i]+2][up[i]+2]==0 &&
YZM[lf[i]+0][up[i]+3]==i && YZM[lf[i]+1][up[i]+3]==i && YZM[lf[i]+2][up[i]+3]==0 &&
YZM[lf[i]+0][up[i]+4]==i && YZM[lf[i]+1][up[i]+4]==i && YZM[lf[i]+2][up[i]+4]==0 )result[i-1]='6';
if( YZM[lf[i]+0][up[i]+0]==i && YZM[lf[i]+1][up[i]+0]==i && YZM[lf[i]+2][up[i]+0]==i &&
YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==0 && YZM[lf[i]+2][up[i]+1]==0 &&
YZM[lf[i]+0][up[i]+2]==0 && YZM[lf[i]+1][up[i]+2]==0 && YZM[lf[i]+2][up[i]+2]==0 &&
YZM[lf[i]+0][up[i]+3]==0 && YZM[lf[i]+1][up[i]+3]==0 && YZM[lf[i]+2][up[i]+3]==0 &&
YZM[lf[i]+0][up[i]+4]==0 && YZM[lf[i]+1][up[i]+4]==0 && YZM[lf[i]+2][up[i]+4]==0 )result[i-1]='7';
if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==0 && YZM[lf[i]+2][up[i]+0]==i &&
YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==i && YZM[lf[i]+2][up[i]+1]==i &&
YZM[lf[i]+0][up[i]+2]==i && YZM[lf[i]+1][up[i]+2]==i && YZM[lf[i]+2][up[i]+2]==0 &&
YZM[lf[i]+0][up[i]+3]==0 && YZM[lf[i]+1][up[i]+3]==i && YZM[lf[i]+2][up[i]+3]==i &&
YZM[lf[i]+0][up[i]+4]==0 && YZM[lf[i]+1][up[i]+4]==0 && YZM[lf[i]+2][up[i]+4]==i )result[i-1]='8';
if( YZM[lf[i]+0][up[i]+0]==0 && YZM[lf[i]+1][up[i]+0]==0 && YZM[lf[i]+2][up[i]+0]==i &&
YZM[lf[i]+0][up[i]+1]==0 && YZM[lf[i]+1][up[i]+1]==i && YZM[lf[i]+2][up[i]+1]==i &&
YZM[lf[i]+0][up[i]+2]==i && YZM[lf[i]+1][up[i]+2]==i && YZM[lf[i]+2][up[i]+2]==0 &&
YZM[lf[i]+0][up[i]+3]==i && YZM[lf[i]+1][up[i]+3]==i && YZM[lf[i]+2][up[i]+3]==0 &&
YZM[lf[i]+0][up[i]+4]==0 && YZM[lf[i]+1][up[i]+4]==i && YZM[lf[i]+2][up[i]+4]==i )result[i-1]='9';
}
printf("%s",result);//輸出識別結果  回復  更多評論
  
# re: 簡單的bmp驗證碼識別 (c++) 2013-04-07 23:07 聶文龍
1:BMP文件組成

  BMP文件由文件頭、位圖信息頭、顏色信息和圖形數據四部分組成。
  2:BMP文件頭(14字節)
  BMP文件頭數據結構含有BMP文件的類型、文件大小和位圖起始位置等信息。
  其結構定義如下:
  typedef struct tagBITMAPFILEHEADER
  {
  WORD bfType; // 位圖文件的類型,必須為BM(1-2字節)
  DWORD bfSize; // 位圖文件的大小,以字節為單位(3-6字節)
  WORD bfReserved1; // 位圖文件保留字,必須為0(7-8字節)
  WORD bfReserved2; // 位圖文件保留字,必須為0(9-10字節)
  DWORD bfOffBits; // 位圖數據的起始位置,以相對于位圖(11-14字節)
  // 文件頭的偏移量表示,以字節為單位
  } BITMAPFILEHEADER;
  3:位圖信息頭(40字節)
  BMP位圖信息頭數據用于說明位圖的尺寸等信息。
  typedef struct tagBITMAPINFOHEADER{
  DWORD biSize; // 本結構所占用字節數(15-18字節)
  LONG biWidth; // 位圖的寬度,以像素為單位(19-22字節)
  LONG biHeight; // 位圖的高度,以像素為單位(23-26字節)
  WORD biPlanes; // 目標設備的級別,必須為1(27-28字節)
  WORD biBitCount;// 每個像素所需的位數,必須是1(雙色),(29-30字節)
  // 4(16色),8(256色)或24(真彩色)之一
  DWORD biCompression; // 位圖壓縮類型,必須是 0(不壓縮),(31-34字節)
  // 1(BI_RLE8壓縮類型)或2(BI_RLE4壓縮類型)之一
  DWORD biSizeImage; // 位圖的大小,以字節為單位(35-38字節)
  LONG biXPelsPerMeter; // 位圖水平分辨率,每米像素數(39-42字節)
  LONG biYPelsPerMeter; // 位圖垂直分辨率,每米像素數(43-46字節)
  DWORD biClrUsed;// 位圖實際使用的顏色表中的顏色數(47-50字節)
  DWORD biClrImportant;// 位圖顯示過程中重要的顏色數(51-54字節)
  } BITMAPINFOHEADER;
  4:顏色表
  顏色表用于說明位圖中的顏色,它有若干個表項,每一個表項是一個RGBQUAD類型的結構,定義一種顏色。RGBQUAD結構的定義如下:
  typedef struct tagRGBQUAD {
  BYTE rgbBlue;// 藍色的亮度(值范圍為0-255)
  BYTE rgbGreen; // 綠色的亮度(值范圍為0-255)
  BYTE rgbRed; // 紅色的亮度(值范圍為0-255)
  BYTE rgbReserved;// 保留,必須為0
  } RGBQUAD;
  顏色表中RGBQUAD結構數據的個數有biBitCount來確定:
  當biBitCount=1,4,8時,分別有2,16,256個表項;
  當biBitCount=24時,沒有顏色表項。
  位圖信息頭和顏色表組成位圖信息,BITMAPINFO結構定義如下:
  typedef struct tagBITMAPINFO {
  BITMAPINFOHEADER bmiHeader; // 位圖信息頭
  RGBQUAD bmiColors[1]; // 顏色表
  } BITMAPINFO;
  5:位圖數據
  位圖數據記錄了位圖的每一個像素值,記錄順序是在掃描行內是從左到右,掃描行之間是從下到上。位圖的一個像素值所占的字節數:
  當biBitCount=1時,8個像素占1個字節;
  當biBitCount=4時,2個像素占1個字節;
  當biBitCount=8時,1個像素占1個字節;
  當biBitCount=24時,1個像素占3個字節;
  Windows規定一個掃描行所占的字節數必須是
  4的倍數(即以long為單位),不足的以0填充,
  biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight;

這個在百度百科大家可以做個詳細的了解,這里就不多說了。

驗證碼圖片:

1.預處理:

接下來就是對圖片進行讀取,

2.灰度化:

對其各個像素的GBR值進行灰度化處理,我用的公式是求平均值的方法,比較方便,效果也還不錯。,將求得的平均值存入一個二維數組中,方便后面的處理。

3.二值化

二值化有很多種算法,我試了試大津法(最大方差法)還有自適應閾值法,不知道什么原因,二值化出來的圖片簡直把圖片就給毀了。。。。最后還是將圖片的GBR信息打印出來,自己找了個臨界值,效果還不錯,二值化之后的噪點也只有幾個。后面的處理就方便多了。將大于這個臨界值的像素點設為255,小于的設為0.出來的圖像就完全黑白二值了。

4.去噪

去噪,看了看幾張驗證碼,發現字母組成最少的就是i,j上面的點了,4個像素點組成,于是將圖片中一個像素點周圍的8個點進行判斷,如果這個點周圍有不超過三個黑點,則證明這個點為噪點,需要去除,需要注意的是邊界周圍只有5個點,需要進行判斷是否為邊界。



5.分割

因為我們的BMP圖片是有間據的,所以分割就用最簡單的一種了

先將去噪完后的BMP像素數組在按自左向右的基礎上從上往下找,找到的第一個黑點即為字符的左邊界,再向右,找到的一列全不為黑點的話,則上一列即為字符的右邊界,再在左邊界和右邊界的區間下,從下往上按行遍歷,找到字符的下邊界,再從上往下找到上邊界,其他字符同理。就可以將圖片分割出來了。



6.識別

我用的識別方法還是比較麻煩的,先是對驗證碼的字符進行輸出(0,1組成),然后再找他們之間的不同相似之處,所以不是很方便,這里也就不細提了,看代碼就應該理解了。

下面是代碼:


view plainprint?
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;
void OtsuThreshold(int *p_data)
{
int i,j;
int nWidth = 70;
int nHeight = 30;
for(j = 0;j < nHeight; j++)
{//二值化實現
for(i = 0; i < nWidth ; i++)
{
if(p_data[j * nWidth +i] < 38)
p_data[j * nWidth + i] = 0;
else
p_data[j * nWidth + i] = 255;
}
}

}
void Print(int graph[][70])
{ int i,j;
for(i = 0; i < 30; i++)
{ for(j = 0; j < 70; j++)
{
if(graph[i][j] == 255)
{
// printf(" ");
graph[i][j] = 1;
}
// else printf("*");
}
// printf("\n");
}


// printf("\n\n\n\n");
}
void duibi(int z[][20][20],int bl1,int br1,int bl2,int br2,int bl3,int br3,int bl4,int br4)
{
int jianju[4];
int i;
jianju[0] = br1-bl1+1;
jianju[1] = br2-bl2+1;
jianju[2] = br3-bl3+1;
jianju[3] = br4-bl4+1;
for(i = 0; i < 4; i++)
{
if(jianju[i] <= 5)//<=5 字符占 小于等于5列的
{
if(jianju[i] == 5)
{
if(z[i][0][0] == 0)
printf("r");
else if(z[i][0][1] == 1)
printf("i");
else printf("t");
}
else printf("i");
}
else if(jianju[i] == 6)//6 字符占 6列
{
if(z[i][0][1] == 0)
{
if(z[i][0][4] == 1)
printf("t");
else printf("s");
}
else if(z[i][0][2] == 1)
printf("j");
else if(z[i][0][4] == 1)
printf("t");
else printf("f");
}
else if(jianju[i] == 7)//7
{
if(z[i][0][0] == 1 && z[i][0][1] == 1 && z[i][0][2] == 1)
{
if(z[i][0][6] == 1)
printf("e");
else if(z[i][0][3] == 0 && z[i][0][4] == 0 && z[i][0][5] == 0 && z[i][0][6] == 0)printf("c");
else printf("f");
}
else if(z[i][0][0] == 1 && z[i][0][1] == 1)
{
if(z[i][0][5] == 1)
{
if(z[i][1][1] == 1)
printf("t");
else printf("e");
}
else if(z[i][0][4] == 1)
printf("r");
else printf("f");
}
else if(z[i][0][0] == 1)
{
printf("3");
}
else if(z[i][0][2] == 1 && z[i][0][3] == 0)
printf("p");
else if(z[i][0][0] == 0 && z[i][0][1] == 0 && z[i][0][2] == 0)printf("z");
else if(z[i][0][0] == 0 && z[i][0][1] == 0 && z[i][0][2] == 1 && z[i][0][3] == 1) printf("h");
else printf("r");
}
else if(jianju[i] == 8)//8
{
if(z[i][0][0] == 1 && z[i][1][1] == 1 && z[i][2][2] == 1 && z[i][3][3] == 1)
printf("j");
else if(z[i][0][0] == 1 && z[i][1][1] == 1 && z[i][2][2] == 1)
printf("d");
else if(z[i][0][0] == 1 && z[i][1][1] == 1)
{
if(z[i][3][3] == 1 &&z[i][4][4] == 1)
printf("c");
else if(z[i][3][3] == 1 && z[i][4][4] ==0)printf("a");
else printf("f");
}
else if(z[i][0][0] == 1 && z[i][2][2] == 1)
{
if(z[i][1][0] == 0) printf("3");
else if(z[i][4][4] == 1 && z[i][0][1] == 0)
printf("2");
else printf("a");
}
else if(z[i][0][0] == 1 && z[i][6][6] == 1)
printf("c");
else if(z[i][0][0] == 1 && z[i][3][3] == 1)
printf("g");
else if(z[i][0][0] == 1)
printf("s");
else if(z[i][1][1] == 1)
printf("2");
else if(z[i][2][2] != 1)
printf("n");
else if(z[i][4][4] == 1)
{
if(z[i][5][5] == 1) printf("u");
else printf("7");
}
else if(z[i][7][7] == 1) printf("z");
else printf("h");

}
else if(jianju[i] == 9) //9
{
if(z[i][0][0] == 1 && z[i][3][3] == 1 && z[i][4][4] == 0 && z[i][5][5] == 0 && z[i][6][6] == 0 && z[i][7][7] ==0 && z[i][8][8] == 0)
printf("a");
else if(z[i][2][2] == 0 && z[i][3][3] ==1 && z[i][4][4] == 0 && z[i][5][5] == 0 && z[i][6][6] == 1 && z[i][7][7] == 1)
printf("e");
else if(z[i][0][0] == 1 && z[i][1][1] == 1 &&z[i][2][2]== 1)
printf("d");
else if(z[i][1][1] == 0 && z[i][2][2] == 0 && z[i][4][4] ==1 && z[i][5][5] == 1 && z[i][7][7] == 0)
{
if(z[i][0][5] == 1) printf("g");
else if(z[i][0][7] == 0) printf("n");
else printf("p");
}
else if(z[i][0][0] ==0 && z[i][1][1] == 0 && z[i][3][3] == 1 && z[i][4][4] == 1 && z[i][5][5] == 0 && z[i][7][7] ==1 && z[i][8][8] ==1)
{
if(z[i][0][3] == 1 && z[i][10][0] == 0)
printf("v");
else if(z[i][0][3] == 1 && z[i][10][0] == 1) printf("y");
else printf("7");
}
else if(z[i][0][0] == 0 && z[i][1][1] ==0 && z[i][2][2] ==0 && z[i][3][3] ==1 &&z[i][4][4] == 1)
{
printf("y");
}
else if(z[i][0][0] == 1 && z[i][1][1] ==1 && z[i][2][2] == 0)
{
if(z[i][3][3] == 1)printf("6");
else if(z[i][4][4] == 1)printf("k");
else if(z[i][5][5] == 1 && z[i][12][4] == 1)printf("h");
else if(z[i][12][4] == 0 && z[i][12][2] == 1) printf("b");
else printf("s");
}
else if(z[i][0][0] == 1 && z[i][1][1] == 0)
{
if(z[i][2][2] == 1)
{
if(z[i][4][4] ==1 ) printf("2");
else printf("z");
}
else if(z[i][3][3] == 0)printf("r");
else printf("8");
}
else printf("b");
}
else if(jianju[i] == 10) //10
{
if(z[i][0][0] == 1 && z[i][1][1] == 1 && z[i][2][2] == 1)
{
if(z[i][3][3] == 1) printf("d");
else printf("x");
}
else if(z[i][0][0] == 1 && z[i][1][1] == 1 && z[i][2][2] == 0)
{
if(z[i][3][3] == 1 && z[i][10][0] == 0)printf("3");
else if(z[i][3][3] == 1 && z[i][10][0] == 1) printf("2");
else if(z[i][4][4] == 1 && z[i][5][5] == 1 && z[i][6][6] ==1)printf("p");
else if(z[i][6][6] == 1) printf("b");
else if(z[i][5][5] == 1) printf("k");
else if(z[i][9][9] == 1) printf("5");
else if(z[i][4][4] == 1) printf("6");
else printf("8");
}
else if(z[i][0][0] == 1 && z[i][1][1] == 0)
{
if(z[i][2][2] == 0)printf("n");
else if(z[i][4][4] == 1) printf("7");
else printf("z");
}
else if(z[i][5][5] == 0) printf("x");
else if(z[i][12][0] == 1) printf("y");
else printf("v");
}


else if(jianju[i] == 11)//11
{
if(z[i][0][2] == 1)
{
if(z[i][0][3] == 1)
printf("4");
else printf("2");
}
else if(z[i][0][8] == 1)
printf("x");
else printf("u");
}
else if(jianju[i] >=13)//>=13
{
if(jianju[i] == 13||jianju[i] == 15)
{
if(jianju[i] == 15)
if(z[i][0][0] == 1) printf("m");
else printf("w");
}
else if(jianju[i] == 14)
printf("m");
else if(z[i][0][0] == 1)
printf("m");
else printf("w");
}
}
}
int main(int argc,char *argv[])
{
FILE *fp;
int rgb[30*70];
int gbr[30 * (70*3)];
int graph[30][70] = {0};
int g=0,b=0,r=0;
int i,j,n,a,d,k;
int Height,Width;
int bl1=0,br1=0,bg1=0,bd1=0,bl2=0,br2=0,bg2=0,bd2=0;
int bl3=0,br3=0,bg3=0,bd3=0,bl4=0,br4=0,bg4=0,bd4=0;
int z[4][20][20] = {0};
// memset(z,0,sizeof(z));
fp = fopen(argv[1],"rb");
if(fp == NULL)
cout<<"read error!";
fseek(fp,18,0);
fread(&Width,4,1,fp);
fread(&Height,4,1,fp);
fseek(fp,54,0);
for(i = 0; i < 30 ; i++)//讀取圖片進行灰度化并保存到rgb數組
{ for(j = 0; j < 70; j++)
{
fread(&g,1,1,fp);
fread(&b,1,1,fp);
fread(&r,1,1,fp);
rgb[i * 70 + j]=(g+b+r)/3;//灰度化
}
fseek(fp,2,1);
}
OtsuThreshold(rgb);//二值化
for(i = 0; i < Height; i++)
{
for(j = 0; j < Width; j++)
graph[30-1-i][j] = rgb[i * 70 + j];
}


//輸出圖片
Print(graph);
//去噪

for(i = 0; i < 30; i++)
{
for(j = 0; j < 70; j++)
{ n = 0;
if(i == 29)
{
if(graph[i][j] == 0 && graph[i][j-1] != 0 && graph[i][j+1] != 0 && graph[i-1][j] != 0)
graph[i][j] = 1;
if(graph[i][j] == 0)
{ n = 0;
if(graph[i-1][j-1] == 0 )
n++;
if(graph[i-1][j] == 0)
n++;
if(graph[i-1][j+1] == 0)
n++;
if(graph[i][j-1] == 0)
n++;
if(graph[i][j+1] == 0)
n++;
if(n < 2)
graph[i][j] = 1;
}
}
/* if(graph[i][j] == 0 && graph[i-1][j-1] != 0 && graph[i-1][j] != 0 && graph[i-1][j+1] != 0 && graph[i][j-1] != 0 && graph[i][j+1] != 0 && graph[i+1][j-1] != 0 && graph[i+1][j] != 0 && graph[i+1][j+1] != 0)
graph[i][j] = 1;
*/
if(graph[i][j] == 0)
{ n = 0;
if(graph[i-1][j-1] == 0 )
n++;
if(graph[i-1][j] == 0)
n++;
if(graph[i-1][j+1] == 0)
n++;
if(graph[i][j-1] == 0)
n++;
if(graph[i][j+1] == 0)
n++;
if(graph[i+1][j-1] == 0)
n++;
if(graph[i+1][j] == 0)
n++;
if(graph[i+1][j+1] == 0)
n++;
if(n < 2)
graph[i][j] = 1;
}


}
}


//第一個字符
// 左邊界
for(i = 0; i < Width; i++)
{
for(j = 0; j < Height; j++)
{
if(graph[j][i] == 0)
{
bl1 = i;
break;
}
}
if(bl1 != 0)
break;
}
//右邊界
for(i = bl1; i < Width; i++)
{
n = 0;
for(j = 0; j < Height; j++)
{
if(graph[j][i] == 0)
n++;
}
if(n == 0)
{
br1 = i-1;
break;
}
}
//上邊界
for(i = 0; i < Height; i++ )
{ n = 0;
for(j = bl1; j <= br1; j++)
{
if(graph[i][j] == 0)
{ bg1 = i;
n = 1;
break;
}
}
if(n != 0)
break;
}
//下邊界
for(i = Height-1; i >= 0 ; i--)
{
n = 0;
for(j = bl1; j <= br1; j++)
{
if(graph[i][j] == 0)
{
bd1 = i;
n = 1;
break;
}
}
if(n != 0)
break;
}
//第二個字符
//左邊界
for(i = br1+1; i < Width; i++)
{
for(j = 0; j < Height; j++)
{
if(graph[j][i] == 0)
{
bl2 = i;
break;
}
}
if(bl2 != 0)
break;
}
//右邊界
for(i = bl2; i < Width; i++)
{
n = 0;
for(j = 0; j < Height; j++)
{
if(graph[j][i] == 0)
n++;
}
if(n == 0)
{
br2 = i-1;
break;
}
}
//上邊界
for(i = 0; i < Height; i++)
{ n = 0;
for(j = bl2; j <= br2; j++)
{
if(graph[i][j] == 0)
n++;
}
if(n != 0)
{ bg2 = i;
break;
}
}
//下邊界
for(i = Height-1; i >= 0; i--)
{ n = 0;
for(j = bl2; j <= br2; j++)
{
if(graph[i][j] == 0)
{ n = 1;
bd2 = i;
}
}
if(n != 0)
break;
}
//第三個字符
//左邊界
for(i = br2+1; i < Width; i++)
{
for(j = 0; j < Height; j++)
{
if(graph[j][i] == 0 )
{ bl3 = i;
break;
}
}
if(bl3 != 0)
break;
}
//右邊界
for(i = bl3; i < Width; i++)
{ n = 0;
for(j = 0; j < Height; j++)
{
if(graph[j][i] == 0)
{
n++;
break;
}
}
if(n == 0)
{
br3 = i-1;
break;
}
}
//上邊界
for(i = 0; i < Height; i++)
{
for(j = bl3; j <= br3; j++)
{
if(graph[i][j] == 0)
{
bg3 = i;
break;
}
}
if(bg3 != 0)
break;
}
//下邊界
for(i = Height-1; i >= 0; i--)
{ n = 0;
for(j = bl3; j <= br3; j++)
{
if(graph[i][j] == 0)
{ n = 1;
bd3 = i;
break;
}
}
if(n != 0)
break;
}
//第四個字符
//左邊界
for(i = br3+1; i < Width; i++)
{
for(j = 0; j < Height; j++)
{
if(graph[j][i] == 0)
{
bl4 = i;
break;
}
}
if(bl4 != 0)
break;
}
//右邊界
for(i = bl4; i < Width; i++)
{ n = 0;
for(j = 0; j < Height; j++)
{
if(graph[j][i] == 0)
{ n++;
break;
}
}
if(n == 0)
{
br4 = i-1;
break;
}
}
if(i == Width)
br4 = Width-1;
//上邊界
for(i = 0; i < Height; i++)
{
for(j = bl4; j <= br4; j++)
{
if(graph[i][j] == 0)
{
bg4 = i;
break;
}
}
if(bg4 != 0)
break;
}
//下邊界
for(i = Height-1; i >= 0; i--)
{ n = 0;
for(j = bl4; j <= br4; j++)
{
if(graph[i][j] == 0)
{
n = 1;
bd4 = i;
break;
}
}
if(n != 0)
break;


}


//輸出圖片
//Print(graph);
//將圖片保存到z【】【】【】數組中
for(i = bg1,a = 0;i <= bd1; i++,a++)
{ for(j = bl1,d = 0; j <= br1; j++,d++)
z[0][a][d] = graph[i][j];
}
for(i = bg2,a = 0;i <= bd2; i++,a++)
{ for(j = bl2,d = 0; j <= br2; j++,d++)
z[1][a][d] = graph[i][j];
}
for(i = bg3,a = 0;i <= bd3; i++,a++)
{ for(j = bl3,d = 0; j <= br3; j++,d++)
z[2][a][d] = graph[i][j];
}
for(i = bg4,a = 0;i <= bd4; i++,a++)
{ for(j = bl4,d = 0; j <= br4; j++,d++)
z[3][a][d] = graph[i][j];
}
duibi(z,bl1,br1,bl2,br2,bl3,br3,bl4,br4);
printf("\n");
/* for(i = 0; i < 4; i++)
{
for(j = 0; j < 20; j++)
{ for(k = 0; k < 20; k++)
if(z[i][j][k] == 1)
printf("1,");
else printf("0,");
printf("\n");
}
printf("\n");
}
*/
cout<<endl;
fclose(fp);
return (0);
}
  回復  更多評論
  

只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
      <noscript id="pjuwb"></noscript>
            <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
              <dd id="pjuwb"></dd>
              <abbr id="pjuwb"></abbr>
              最新日韩欧美| 国产热re99久久6国产精品| 久久在线视频| 欧美精品网站| 亚洲黑丝一区二区| 国产日韩在线不卡| 一区二区三区四区五区精品| 亚洲精品在线一区二区| 99pao成人国产永久免费视频| 一区二区三区日韩精品| 欧美日韩一视频区二区| 亚洲一区二区三区成人在线视频精品 | 欧美电影资源| 麻豆久久精品| 久久国产欧美精品| 国产一区在线视频| 蜜臀久久99精品久久久画质超高清| 一本色道久久| 亚洲福利av| 久久精品在线播放| 1769国产精品| 欧美特黄一级| 国产精品草草| 久久久精品欧美丰满| 欧美高清视频在线播放| 亚洲一区综合| 麻豆国产精品va在线观看不卡| 亚洲丰满在线| 91久久久精品| 亚洲精品午夜| 伊人狠狠色j香婷婷综合| 国产精品xxxxx| 国产网站欧美日韩免费精品在线观看 | 亚洲欧美日韩国产中文在线| 欧美有码视频| 亚洲精品一品区二品区三品区| 亚洲性图久久| 午夜日韩视频| 亚洲图片欧美午夜| 日韩视频在线观看一区二区| 久久性色av| 国产精品亚洲一区二区三区在线| 欧美在线观看视频| 欧美午夜片欧美片在线观看| 亚洲一级在线| 欧美综合国产| 欧美三级网页| 国产美女搞久久| aaa亚洲精品一二三区| 日韩午夜高潮| 午夜一区不卡| 久久久久久国产精品一区| 亚洲一区二区精品在线| 亚洲专区在线| 亚洲国产精品va在线看黑人 | 午夜欧美电影在线观看| 亚洲精品三级| 宅男精品视频| 国产欧美日韩伦理| 亚洲精品日韩综合观看成人91| 亚洲国产欧美日韩| 欧美在线视频观看| 亚洲国产高清自拍| 性欧美大战久久久久久久久| 亚洲视频在线一区| 欧美日韩国产不卡| 亚洲免费精品| 亚洲激情一区二区三区| 亚洲男女自偷自拍图片另类| 欧美激情麻豆| 一本色道综合亚洲| 亚洲娇小video精品| 亚洲欧美国产一区二区三区| 久久久精品一区| 亚洲欧美日韩综合| 欧美日韩1区2区3区| 亚洲国产片色| 国产亚洲激情在线| 久久综合图片| 欧美高清在线精品一区| 亚洲电影免费在线观看| 亚洲午夜精品久久久久久app| 亚洲经典自拍| 欧美福利一区二区三区| 一区二区精品| 欧美一区2区视频在线观看 | 亚洲人体影院| 久热国产精品视频| 男人的天堂成人在线| 在线观看视频亚洲| 久久国产成人| 亚洲一区二区三区视频| 老司机久久99久久精品播放免费 | 欧美日韩久久精品| 日韩午夜免费视频| 久久久久久香蕉网| 亚洲一区二三| 蜜桃av噜噜一区二区三区| 欧美一区二区黄| 亚洲免费高清| 久久午夜视频| 欧美一级夜夜爽| 欧美精品在线免费观看| 欧美日韩美女一区二区| 午夜视频一区在线观看| 免费观看成人鲁鲁鲁鲁鲁视频 | 久久亚洲精品一区二区| 一本色道**综合亚洲精品蜜桃冫 | 亚洲午夜国产成人av电影男同| 亚洲午夜激情网站| 亚洲性视频网站| 蜜臀久久久99精品久久久久久| 老牛嫩草一区二区三区日本| 亚洲日本va午夜在线电影| 欧美一区二区三区免费大片| 久久一区二区三区四区五区| 日韩西西人体444www| 亚洲欧美综合另类中字| 久久国产婷婷国产香蕉| 国产专区一区| 欧美精品二区| 久久精品国产91精品亚洲| 久久精品女人的天堂av| 在线视频你懂得一区| 欧美日韩综合视频| 久久免费偷拍视频| 这里只有视频精品| 久久久久国产精品厨房| 一区二区在线不卡| 欧美久久久久免费| 欧美在线不卡| 欧美日韩综合另类| 欧美三级乱人伦电影| 欧美资源在线观看| 欧美中文日韩| 国产一区二区丝袜高跟鞋图片| 欧美午夜免费| 国产精品毛片大码女人| 老司机午夜精品视频| 久久久国产精彩视频美女艺术照福利 | 亚洲香蕉网站| 国产一区二区三区免费观看| 欧美另类综合| 国产精品另类一区| 欧美精品在线观看91| 欧美激情一区二区三区在线视频 | 国产精品国产三级国产aⅴ无密码| 一级成人国产| 国产欧美一区二区白浆黑人| 欧美日韩日本国产亚洲在线| 欧美电影在线观看完整版| 欧美成人性网| 亚洲高清激情| 在线观看日产精品| 中文国产成人精品久久一| 久久国产日韩欧美| 久久九九精品99国产精品| 久久躁日日躁aaaaxxxx| 亚洲电影成人| 亚洲在线一区| 亚洲美女区一区| 亚洲天堂视频在线观看| 一区电影在线观看| 久久亚洲影音av资源网| 亚洲免费中文| 久久成人综合视频| 极品日韩av| 欧美一区二区性| 亚洲欧洲精品一区二区三区不卡| 亚洲激情网址| 中文精品视频| 极品裸体白嫩激情啪啪国产精品| 夜夜夜久久久| 欧美一级大片在线观看| 亚洲第一色在线| 国产精品99一区| 亚洲国产精品一区在线观看不卡| 欧美在线资源| 久久精品免费电影| 亚洲男人的天堂在线| 欧美日韩中文字幕精品| 亚洲综合大片69999| 亚洲午夜精品一区二区| 久久精品99国产精品日本| 国产精品麻豆欧美日韩ww | 欧美~级网站不卡| 韩国三级电影一区二区| 中文一区字幕| 久久成人18免费观看| 伊人男人综合视频网| 亚洲激情欧美激情| 欧美va亚洲va国产综合| 亚洲一级二级在线| 久久久久免费| 亚洲黄色天堂| 欧美亚洲日本网站| 亚洲国产高清一区二区三区| 久久天天躁狠狠躁夜夜av| 欧美成年人网站| 亚洲欧美在线磁力|