识别图片中的数字------基本思路


1. 读取矩阵     拿到一张带有数字的图片后,首先就是得到它的rgb矩阵。这对于bmp格式文件来说易如反掌,对于jpg的相对麻烦一些。假设我们现在已经得到了rgb矩阵M(m*n),每个点都有三个属性(r,g,b)。

2. 灰度化       将彩色图片转化为灰色图片,目的在于使图片颜色初步单调,便于第3步的进行。实现方法很多,可以将每个点的(r,g,b)设置为(a*r+b*g+c*b),其中a+b+c=1,即加权平均;也可以设置为(max(r,g,b))或者(min(r,g,b))。我采用的是加权法:0.11*blue+0.59*green+0.30*red。

3. 黑白化       将灰色图片转化为黑白图片。这一步是为了后期处理上的方便。大多数人会选择阈值法:设置一个(rx,gx,bx),逐点比较,将所有的点设置为黑色(0,0,0)或者白色(255,255,255)。我采用的方法是:

view plaincopy to clipboardprint?
 //转为黑白两色   
 void CMyBitMap::Trans1()   
 {   
     ColorData color;   
     for(int i=0;i<width*height;i++)   
     {   
         color=color_data[i];   
         int max=100;   
         if((color.blue < max) || (color .green<max) || (color.red <max))   
         {   
             color.blue=0;   
             color.green=0;   
             color.red=0;   
         }   
         else  
         {   
             color.blue=255;   
             color.green=255;   
             color.red=255;   
         }   
         color_data[i]=color;   
     }   
 }  
 //转为黑白两色
 void CMyBitMap::Trans1()
 {
  ColorData color;
  for(int i=0;i<width*height;i++)
  {
   color=color_data[i];
   int max=100;
   if((color.blue < max) || (color .green<max) || (color.red <max))
   {
    color.blue=0;
    color.green=0;
    color.red=0;
   }
   else
   {
    color.blue=255;
    color.green=255;
    color.red=255;
   }
   color_data[i]=color;
  }
 }

4. 腐蚀和膨胀      考虑到图片中的数字笔画可能比较细,同时有一些噪音斑点存在,需要进行腐蚀处理+膨胀处理。腐蚀处理就是把图片中线条变得更细,就像白色部分在腐蚀黑色部分一样;膨胀处理就是把线条变粗。在腐蚀的过程中,会把孤立的噪音点清除掉;在膨胀的过程中,会把原有的线条更圆润、清晰。所以需要先进行腐蚀,后进行膨胀。我实现的腐蚀算法很简单:遍历rgb矩阵,如果当前像素点为白色,则将其四周的像素点设置为白色。膨胀算法类似:遍历矩阵,如果当前点为黑色像素,则将其四周的点全部设置为黑色。我们不妨设想一下,如果先进行膨胀,后进行腐蚀,会有什么效果呢?

5.分割       这一步把图片分割为若干块,每一块中都是独立的一个数字,说的数学化一点,就是找到所有数字的外切矩形。如果各个数字没有粘连,则很容易分割;如果数字之间有粘连,则比较困难。常用的方法是检测粘连点(有点类似于图论中的连接两个连通块的桥),然后分割。我找的实验图片都是不粘连的,算是避过了这个难题  :)

6.离散化    第5步以后,我们得到若干矩形,每个矩形中有且仅有一个数字,现在针对每个矩形进行处理。常用的思路是将该矩形映射为一个二维矩阵。我的办法是映射为一个8*8的01矩阵,其中0代表白色块,1代表黑色块,实现方法比较无脑:将矩形分割为8*8的小矩形,然后统计每个小矩形中的黑色块数,如果超过小矩形面积的一半,则认为该小矩形全部为黑色,否则全为白色。下图是该8*8矩阵打印的结果,你能猜出来是哪个数字么?

1 1 1 1 1 1
 1 1 1
 1 1           1
   1 1 1 1 1 1 1
               1
               1
               1
             1 1

7.训练    这时我情不自禁的想到了神经网络方法,输入是8*8=64,输出是0到9,共10个。事实上,很多人会采用神经网络方法来训练,可以很方便的得到识别结果的可信率。另一种方法是使用决策树,举个例子:如果8*8的矩阵的第一行有大于5个点为黑色,则该数字很有可能0、2、3、5、7、8、9,一般不会是1和4;如果第一行只有1到2个黑色,则很有可能是1和1;如果第一行有3到4个黑色,则很有可能是0和4(如果这些黑色连续,则很有可能是0;否则可能是4)。构造数十条此类的判断,也可以实现识别功能。如果为每个规则设置一个可信度,这样得到的识别结果也会附加一个可信度。

   上面所说的,只是图像处理和模式识别中最最简单的东西,涉足以后,才发现博大精深呢   :)