随笔 - 298  文章 - 377  trackbacks - 0
<2013年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

常用链接

留言簿(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 聂文龙 阅读(4221) 评论(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   管理