图像预处理第6步:分割,并在分割出来的字符外面画框以标识

//图像预处理第6步:分割,并在分割出来的字符外面画框以标识
void CChildView::OnImgprcDivide() 
{
	m_charRect=CharSegment(m_hDIB);
	//在屏幕上显示位图
	CDC* pDC=GetDC();
	DisplayDIB(pDC,m_hDIB);	
	DrawFrame(pDC,m_hDIB,m_charRect,2,RGB(20,60,200));
}

  

/*************************************************
*
* 函数名称:
*       CharSegment()
*
*  参数:
*      HDIB  hDIB      -原图像的句柄
*
*  返回值:
*     CRectLink        -存放被分割的各个字符位置信息的链表
*
* 功能:
*    将图像中待识别的字符逐个分离出来并返回存放各个字符的位置信息的链表
*
*  说明:
*    此函数只能对2值化后的图像进行处理
*
*********************************************************/


CRectLink CharSegment(HANDLE hDIB)
{
    
    //清空用来保存每个字符区域的链表
    CRectLink charRect1,charRect2;
    charRect1.clear();
    charRect2.clear();

    // 指向DIB的指针
    LPSTR lpDIB=(LPSTR) ::GlobalLock((HGLOBAL)hDIB);
    
    // 指向DIB象素指针
    LPSTR    lpDIBBits;    

    // 找到DIB图像象素起始位置
    lpDIBBits = ::FindDIBBits(lpDIB);
    
    //指向象素的的指针
    BYTE* lpSrc;

    //图像的长度和宽度
    int height,width;

    //获取图像的宽度
    width=(int)::DIBWidth(lpDIB);

    //获取图像的长度
    height=(int)::DIBHeight(lpDIB);

    //计算图像每行的字节数
    LONG    lLineBytes = WIDTHBYTES(width * 8);

    //定义上下边界两个变量
    int top,bottom;

    //象素的灰度值
    int gray; 

    //设置循环变量
    int i,j;

    //用来统计图像中字符个数的计数器
    digicount=0;


    //从上往下扫描,找到上边界

    //
    for (i=0;i<height;i++)
    {
         //
          for (j=0;j<width;j++)
        {
            // 指向图像第i行,第j个象素的指针
            lpSrc = (unsigned char*)lpDIBBits + lLineBytes * i + j;

            //获得该点的灰度值
            gray = *(lpSrc);

            //看是否为黑点
            if (gray == 0)
            {   
               //若为黑点,把此点作为字符大致的最高点
                top = i;

                //对i强行赋值以中断循环
                i=height;

                //跳出循环
                break;
            }

        //如果该点不是黑点,继续循环
        }
    }


    //从下往上扫描,找下边界

    //
    for (i = height-1;i>=0;i--)
    {

        //
        for (j=0;j<width;j++)
        {
            // 指向图像第i行,第j个象素的指针
            lpSrc = (unsigned char*)lpDIBBits + lLineBytes * i + j;

            //获取该点的灰度值
            gray = *(lpSrc);

            //判断是否为黑点
            if (gray == 0)
            {
                //若为黑点,把此点作为字符大致的最低点
                bottom = i;

                //对i强行赋值以中断循环
                i=-1;

                //跳出循环
                break;
            }

          //如果该点不是黑点,继续循环
        }
    
    }

    //lab 用作是否进入一个字符分割的标志
    bool lab = false;

    //表明扫描一列中是否发现黑色点
    bool black = false;

    //存放位置信息的结构体
    CRect rect;

    //计数器置零
    digicount=0;
   
    //
    for (i=0;i<width;i++)
    {
        //开始扫描一列
        black=false;

        for (j=0;j<height;j++)
            {    
                // 指向图像第i行,第j个象素的指针
                lpSrc = (unsigned char*)lpDIBBits + lLineBytes * j + i;

                //获取该点的灰度值
                gray = *(lpSrc);

                //判断是否为黑点
                if (gray == 0)
                {
                    //如果发现黑点,设置标志位
                    black=true;

                    //如果还没有进入一个字符的分割
                    if(lab==false)
                    {    
                        //设置左侧边界
                        rect.left = i;

                        //字符分割开始
                        lab = true;
                    }

                    //如果字符分割已经开始了
                    else

                      //跳出循环
                        break;
                }        
            }

        //如果已经扫到了最右边那列,说明整副图像扫描完毕。退出
           if(i==(width-1))
               
         //退出整个循环       
           break;

        //如果到此black仍为false,说明扫描了一列,都没有发现黑点。表明当前字符分割结束
        if(lab==true&&black==false)
        {   
           //将位置信息存入结构体中

           //设置右边界
            rect.right =i;

            //设置上边界
            rect.top =top;

            //设置下边界
            rect.bottom =bottom;

            //将框外括一个象素,以免压到字符
            rect.InflateRect (1,1);

            //将这个结构体插入存放位置信息的链表1的后面
            charRect1.push_back (rect);

            //设置标志位,开始下一次的字符分割
            lab=false;

            //字符个数统计计数器加1
            digicount++;
            
        }

        //进入下一列的扫描

    }

   //再将矩形轮廓矩形的top和bottom精确化

    //将链表1赋值给链表2
    charRect2=charRect1;

    //将链表2的内容清空
    charRect2.clear ();

    //建立一个新的存放位置信息的结构体
    CRect rectnew;

    //对于链表1从头至尾逐个进行扫描
    while(!charRect1.empty())
    {    
        //从链表1头上得到一个矩形
        rect= charRect1.front();

        //从链表1头上面删掉一个
        charRect1.pop_front();

        //计算更加精确的矩形区域

        //获得精确的左边界
        rectnew.left =rect.left-1 ;

        //获得精确的右边界
        rectnew.right =rect.right+1 ;

        //通过获得的精确左右边界对上下边境重新进行精确定位

        // 由下而上扫描计算上边界
        
        //
        for(i=rect.top ;i<rect.bottom ;i++)
        {   
          //
            for(j=rect.left ;j<rect.right ;j++)
            {   
                 // 指向图像第i行,第j个象素的指针
                lpSrc = (unsigned char*)lpDIBBits + lLineBytes * i + j;

                //如果这个象素是黑点
                if (*lpSrc == 0)
                {    
                    //设置上边界
                    rectnew.top = i-1;
                    
                    //对i进行强制定义以跳出循环
                    i=rect.bottom  ;

                    //跳出循环
                    break;
                }    
            }
        }

        //由下而上扫描计算下边界
   
        //
        for(i=rect.bottom-1 ;i>=rect.top  ;i--)
        {
            //
            for(j=rect.left ;j<rect.right ;j++)
            {
                // 指向图像第i行,第j个象素的指针
                lpSrc = (unsigned char*)lpDIBBits + lLineBytes * i + j;

                //该点如果为黑点
                if (*lpSrc == 0)
                {    
                    //设置下边界
                    rectnew.bottom = i+1;

                    //对i进行强制定义以跳出循环
                    i=-1;
                    //跳出循环
                    break;
                }    
            }
        }

        //将得到的新的准确的位置信息从后面插到链表2的尾上
        charRect2.push_back (rectnew);
    }

    //将链表2 传递给链表1
    charRect1=charRect2;
    
    //解除锁定
    ::GlobalUnlock(hDIB);

    //将链表1返回
    return charRect1;
}
/*********************************** ************************************
函数名称:DisplayDIB
参数:
    CDC* pDC            -指向当前设备上下文(Divice Context)的指针
    HDIB hDIB            -要显示的位图的句柄
**********************************************************************/

void DisplayDIB(CDC* pDC,HDIB hDIB)
{
    BYTE* lpDIB=(BYTE*)::GlobalLock((HGLOBAL)hDIB);
    // 获取DIB宽度和高度
    int cxDIB =  ::DIBWidth((char*) lpDIB);
    int cyDIB =  ::DIBHeight((char*)lpDIB);
    CRect rcDIB,rcDest;
    rcDIB.top = rcDIB.left = 0;
    rcDIB.right = cxDIB;
    rcDIB.bottom = cyDIB;
    //设置目标客户区输出大小尺寸
    rcDest = rcDIB;
    //CDC* pDC=GetDC();
    ClearAll(pDC);
    //在客户区显示图像
    //for(int ii=0;ii<10;ii++)
        ::PaintDIB(pDC->m_hDC,rcDest,hDIB,rcDIB,NULL);
    ::GlobalUnlock((HGLOBAL)hDIB);
}
/*****************绘制数字字符外面的矩形框*******************/
void DrawFrame(CDC* pDC,HDIB hDIB, CRectLink charRect,unsigned int linewidth,COLORREF color)
{    
    CPen pen;
    pen.CreatePen (PS_SOLID,linewidth,color);
    pDC->SelectObject (&pen);
    ::SelectObject (*pDC,GetStockObject(NULL_BRUSH));
    CRect rect,rect2;
    BYTE* lpDIB=(BYTE*)::GlobalLock ((HGLOBAL)hDIB);
    while(!charRect.empty())
    {
        
        //从表头上得到一个矩形
        rect2=rect= charRect.front();
        //从链表头上面删掉一个
        charRect.pop_front();
        //注意,这里原先的rect是相对于图像原点(左下角)的,
        //而在屏幕上绘图时,要转换以客户区为原点的坐标
        rect.top =::DIBHeight ((char*)lpDIB)-rect2.bottom;
        rect.bottom =::DIBHeight ((char*)lpDIB)-rect2.top ;
        pDC->Rectangle (&rect);
    }
    ::GlobalUnlock ((HGLOBAL)hDIB);
}

 运行效果:

原文地址:https://www.cnblogs.com/Bobby0322/p/5407964.html