ytop = point.y; if(xrigth < point.x) xrigth = point.x; if(ybottom < point.y) ybottom = point.y; } //这个循环之后,xleft记录了手写数字区域最左边的像素点的x坐标,ytop记录了最上边的像素点的y的坐标
//xrigth记录了最右边的点的x坐标,ybottom记录了最下边的点的y的坐标,这样就可以得到数字区域的宽跟长,以像素为单位
unsigned char * pdata=new unsigned char[w*h*3]; ::memset(pdata,0,w*h*3); for(int i=0;i //这样相当于,将手写数字区域当成一张数字图片,将其从第一行到最后一行的数据顺序读到内存中,所以我们显示时,一定要将此图片数据区域按行转置 unsigned char * ppdata = new unsigned char[w*h*3]; ::memcpy(ppdata,pdata,w*h*3); for(i = 0 ; i < h ; i++) for(int j = 0 ; j < w ; j++) { ppdata[((h-i-1)*w+j)*3] = pdata[(i*w+j)*3]; ppdata[((h-i-1)*w+j)*3+1] = pdata[(i*w+j)*3+1]; ppdata[((h-i-1)*w+j)*3+2] = pdata[(i*w+j)*3+2]; } //注意,windows中一般的位图是倒向的位图,也就是说我们实际看到的图像中,第一行像素值在文件中存储在位图数据区域的最后一行的,而最后一行像素在位图 //文件数据区域矩阵中是存储在第一行的,所以平时我们读取了一个位图之后,读取到的像素值,与我们实际看到的图像的像素是按行倒置的 //并且当我们用StretchDibits函数显示位图时,数据区域指针一定要给成按行倒置的位图数据矩阵 xleft = xleft-1; ytop = ytop-1; xrigth = xrigth + 1; ybottom = ybottom + 1; //将四周扩展了一排像素 nw= xrigth-xleft+1; nh= ybottom-ytop+1; 13 if(nw%4!=0) nw = nw + 4-nw%4; unsigned char* pnum = new unsigned char[nw*nh]; pnumzheng = new unsigned char[nw*nh]; ::memset(pnum,0,nw*nh); for(i = 0 ;i < stuPoint.cur ;i++) { CPoint point; point = *(stuPoint.p+i); int x , y; x = point.x - xleft; y = point.y - ytop; //(xleft,ytop)相当于生成的手写数字区域矩形最左上角的坐标,这里完成了坐标系统的转换 pnum[y*nw+x] = 255; //pnum[(y*nw+x)*3+1] = 255; //pnum[(y*nw+x)*3+2] = 255; } for(i = 0 ;i < nh ;i++) for(int j = 0 ; j < nw ;j++) { pnumzheng[(nh-i-1)*nw+j] = pnum[i*nw+j]; //8bit级图像指针 //pnumzheng[((nh-i-1)*nw+j)*3+1] = pnum[(i*nw+j)*3+1]; //pnumzheng[((nh-i-1)*nw+j)*3+2] = pnum[(i*nw+j)*3+2]; } BYTE * p = BitTo24Bit(pnumzheng , nw , nh); binfo.biSize=sizeof(BITMAPINFOHEADER); binfo.biWidth=nw; binfo.biHeight=nh; binfo.biPlanes=1; binfo.biBitCount=24; binfo.biCompression=BI_RGB; binfo.biSizeImage=nw*nh*3; binfo.biXPelsPerMeter=0; binfo.biYPelsPerMeter=0; binfo.biClrUsed=0; binfo.biClrImportant=0; HDC hdc = ::GetDC(m_ctlShowNum.m_hWnd); ::SetStretchBltMode(hdc,HALFTONE); ::StretchDIBits(hdc, 0,0, nw,nh, 0,0, nw, nh, 14 } p, (LPBITMAPINFO)&binfo, DIB_RGB_COLORS, SRCCOPY ); 3.2数字的特征提取 数字特征的提取是为了更好的实现数字的识别,在此是通过提取所绘制数字的像素特征即如前面所介绍的用目标像素个数除以这个小区域内总得像素个数的结果作为此区域的特征值,共可得到64个特征值。在此通过在函数 Get64Feature(BYTE *pd, int w, int h)中调用函数GetOneFeature(int brow, int erow, int bcol, int ecol , BYTE * pdata ,int w)来实现数字特征的提取功能。 具体实现代码为: 1.得到64个区域的特征值 double* CNumShiBieDlg::Get64Feature(BYTE *pd, int w, int h) { double *pFeature = new double[64]; if((w%8==0) && (h%8==0)) { int hc = h/8; int wc = w/8; int jishu = 0; int j = 0; //列的方向上共8个单元格 while(j < 8)//j = 0说明是第一行的单元格像素,依次类推 { int cc = 0 ; //记录一行方格内的单元格个数,共8个方格所以循环8次 while(cc < 8) { pFeature[jishu++] = this->GetOneFeature(j*hc , (j+1)*hc , cc*wc ,(cc+1)*wc , pd ,w); cc++;//此时已经统计完一个单元格的像素 } j++ ; //此时已经统计完一行单元格内的像素 } }//if((w%8==0) && (h%8==0)) else if((h%8!=0)&&(w%8==0)) { int hc1 = h%8; //行不是8的倍数,最上面那一行单元格单独考虑 int hc = (h-hc1)/8; 15 int wc = w/8; int jishu = 0; int j = 0; //列的方向上共8个单元格 while(j < 8)//j = 0说明是第一行的单元格像素,依次类推 { int cc = 0 ; //记录一行方格内的单元格个数,共8个方格所以循环8次 while(cc < 8) { if(j==0) pFeature[jishu++] = this->GetOneFeature(0 , hc1 , cc*wc , (cc+1)*wc,pd ,w); else pFeature[jishu++] = this->GetOneFeature(hc1+(j-1)*hc , hc1+j*hc ,cc*wc , (cc+1)*wc , pd ,w); cc++; } j++ ; //此时已经统计完一行单元格内的像素 } }//else if((h%8!=0)&&(w%8==0)) else if((w%8!=0)&&(h%8==0)) //行是8的倍数,列不是8的倍数 { int hc = h/8; int wc1 = w%8; int wc = (w-wc1)/8; int jishu = 0; int j = 0; //列的方向上共8个单元格 while(j < 8)//j = 0说明是第一行的单元格像素,依次类推 { int cc = 0 ; //记录一行方格内的单元格个数,共8个方格所以循环8次 while(cc < 8) { if(cc==0) pFeature[jishu++] = this->GetOneFeature(j*hc , (j+1)*hc , 0 , wc1 , pd ,w); else pFeature[jishu++] = this->GetOneFeature(j*hc , (j+1)*hc , wc1+(cc-1)*wc ,wc1+cc*wc ,pd ,w); cc++; } j++ ; //此时已经统计完一行单元格内的像素 } }/*else if((w%8!=0)&&(h%8==0))*/ else if((w%8!=0)&&(h%8!=0)) { int hc1 = h%8; int hc = (h-hc1)/8; 16 int wc1 = w%8; int wc = (w-wc1)/8; int jishu = 0; int j = 0; //列的方向上共8个单元格 while(j < 8)//j = 0说明是第一行的单元格像素,依次类推 { int cc = 0 ; //记录一行方格内的单元格个数,共8个方格所以循环8次 while(cc < 8) { if(j==0&&cc==0) pFeature[jishu++] = this->GetOneFeature(0 , hc1 ,0 , wc1 ,pd ,w); else if(j==0&&cc!=0) pFeature[jishu++] = this->GetOneFeature(0 ,hc1 ,wc1+(cc-1)*wc ,wc1 + cc*wc , pd ,w); else if(j!=0&&cc==0) pFeature[jishu++] = this->GetOneFeature(hc1+(j-1)*hc1 ,hc1+j*hc1 ,0 , wc1 ,pd ,w); else pFeature[jishu++] = this->GetOneFeature(hc1+(j-1)*hc1 , hc1+j*hc1 , wc1+(cc-1)*wc , wc1+cc*wc ,pd ,w); cc++; } j++ ; //此时已经统计完一行单元格内的像素 } }/*else if((w%8!=0)&&(h%8!=0))*/ return pFeature; } 2.得到每个小区域的特征值 double CNumShiBieDlg::GetOneFeature(int brow, int erow, int bcol, int ecol , BYTE * pdata ,int w) { int pixAim = 0; int pixSum = 0; for(int i = brow ; i< erow ; i++) for(int j = bcol ; j < ecol ;j++) if(pdata[i*w+j]==255) pixAim++; else pixSum++; double df; df = (double)pixAim/(double)pixSum; return df; } 17