图像预处理第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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值