Visual C++程序开发范例宝典(软件工程师典藏版)
上QQ阅读APP看书,第一时间看更新

3.6 图像的剪切、合成与识别

实例111 图像的剪切

这是一个可以启发思维的实例

实例位置:光盘\mingrisoft\03\111

实例说明

几乎所有的图像编辑软件都提供了图像的剪切功能,用户可以根据需要剪切图像的某一部分进行处理,本实例也实现了这一功能,运行程序,单击“剪切”按钮,将截取源图像的某一区域到目标图像中,如图3.33所示。

图3.33 图像的剪切

技术要点

实现图像的剪切可以使用CRgn类创建一个剪切的区域,然后利用目标设备上下文CDC选中该剪切的区域,在该区域绘制图像,最后在源设备上下文中将剪切的区域设置为白色的背景。CRgn封装了Windows图像设备接口的区域对象,该区域可以是椭圆形或多边形。用户可以通过该类为CDC定义一个剪切的区域。CRgn类的主要方法如下:

(1)CreateRectRgn方法。该方法用于创建一个矩形区域。语法如下:

BOOL CreateRectRgn( int x1, int y1, int x2, int y2 );

参数说明:

● x1、y1:标识矩形区域的左上角坐标。

● x2、y2:标识矩形区域的右下角坐标。

(2)CreateEllipticRgn方法。该方法用于创建一个椭圆形的区域。语法如下:

BOOL CreateEllipticRgn( int x1, int y1, int x2, int y2 );

参数说明:

● x1、y1:标识椭圆的外接矩形的左上角坐标。

● x2、y2:标识椭圆的外接矩形的右下角坐标。

(3)CreatePolygonRgn方法。该方法用于创建一个多边形的区域。语法如下:

BOOL CreatePolygonRgn( LPPOINT lpPoints, int nCount, int nMode );

参数说明:

● lpPoints:是CPoint类型数组指针,用于标识多边形的顶点坐标。

● nCount:标识lpPoints数组中元素的数量。

● nMode:标识区域的填充模式。

(4)CombineRgn方法。该方法用于组合两个区域。语法如下:

int CombineRgn( CRgn* pRgn1, CRgn* pRgn2, int nCombineMode );

参数说明:

● pRgn1、pRgn2:标识组合区域的两个指针。

● nCombineMode:确定区域的组合模式,可选值如下。

■ RGN_AND:使用两个区域的重叠部分。

■ RGN_COPY:使用第一个区域。

■ RGN_DIFF:创建由第一区域组成的区域,而不包含第二个区域。

■ RGN_OR:使用两个区域的共同区域。

■ RGN_XOR:使用两个区域的非重叠部分。

实现过程

(1)新建一个基于对话框的应用程序。

(2)在对话框中添加图片控件和按钮控件。

(3)主要程序代码如下:

        void CCutImageDlg::OnOK()
        {
            CBitmap m_bitmap;
            HBITMAP m_hbitmap = m_sourceimage.GetBitmap();
            //附加位图句柄
            m_bitmap.Attach(m_hbitmap);
            CDC* m_dc = m_cutimage.GetDC();
            CRgn m_rgn;
            BITMAP m_bitinfo;
            m_bitmap.GetBitmap(&m_bitinfo);
            //创建一个剪切区域
            m_rgn.CreateEllipticRgn(150,1,m_bitinfo.bmWidth-1,m_bitinfo.bmHeight-1);
            CDC* m_sourcedc = m_sourceimage.GetDC();
            //选中剪切区域
            m_dc->SelectClipRgn(&m_rgn,RGN_COPY );
            m_dc->BitBlt(0,0,m_bitinfo.bmWidth,m_bitinfo.bmHeight,m_sourcedc,0,0,SRCCOPY);  //绘制剪切图像
            m_sourcedc->SelectClipRgn(&m_rgn,RGN_COPY );
            m_sourcedc->BitBlt(0,0,m_bitinfo.bmWidth,m_bitinfo.bmHeight, m_dc,0,0,WHITENESS);//绘制源图像
            m_bitmap.Detach();
        }

举一反三

根据本实例,读者可以:

开发具有合成图层功能的图像处理软件。

实例112 图像的合成

本实例是一个提高效率、人性化的程序

实例位置:光盘\mingrisoft\03\112

实例说明

在许多图像处理软件中,都提供了图像的合成功能。例如在PhotoShop中,用户可以利用图层技术合成图像。本实例实现了图像的合成技术,效果如图3.34所示。

图3.34 图像的合成

技术要点

本实例中实现图像合成是采用设备上下文CDC类的BitBlt方法实现的。CDC类提供了多个绘制图像的方法,常用的主要有BitBlt和StretchBlt两个方法,下面分别介绍这两个方法。

(1)BitBlt方法。该方法将位图从源设备区域复制到目标设备区域。语法如下:

BOOL BitBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop );

参数说明:

● x、y:标识目标区域的左上角坐标。

● nWidth、nHeight:确定复制位图的宽度和高度。

● pSrcDC:标识源设备上下文指针。

● xSrc、ySrc:标识源位图的左上角坐标。

● dwRop:确定复制模式,通常为SRCCOPY。

(2)StretchBlt方法。该方法将位图从源设备区域复制到目标设备区域。与BitBlt方法不同的是,StretchBlt方法在必要的时候会延长或压缩位图区域以适合目标区域。语法如下:

BOOL StretchBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop );

参数说明:

● x、y:标识目标区域的左上角坐标。

● nWidth、nHeight:确定复制位图的宽度和高度。

● pSrcDC:标识源设备上下文指针。

● xSrc、ySrc:标识源位图的左上角坐标。

● nSrcWidth、nSrcHeight:标识源位图的宽度和高度。

● dwRop:确定复制模式,通常为SRCCOPY。

实现过程

(1)新建一个基于对话框的应用程序。

(2)向对话框中添加Button和Picture控件。

(3)主要程序代码如下:

          void CCombineImageDlg::OnOK()
          {
              //获取背景图片设备上下文
              CDC* m_grounddc = m_back.GetDC();
              //获取子图片设备上下文
              CDC* m_babydc = m_baby.GetDC();
              CBitmap m_bitmap;                   //位图对象
              BITMAP  m_bitinfo;                 //位图信息
              int m_height,m_width;
              m_bitmap.Detach();
              m_bitmap.Attach((HBITMAP)m_baby.GetBitmap());
              //获取位图大小
              m_bitmap.GetObject(sizeof(m_bitinfo),&m_bitinfo);
              m_width=m_bitinfo.bmWidth;         //图像宽度
              m_height=m_bitinfo.bmHeight;       //图像高度
              //在背景图片的指定区域绘制图像
              m_grounddc->BitBlt(130,100,m_width,m_height,m_babydc,0,0,SRCCOPY);
              //将句柄与位图对象分离
              m_bitmap.Detach();
          }

举一反三

根据本实例,读者可以:

实现图像的剪切。

实例113 获取鼠标任意位置的颜色值

本实例可以方便操作、提高效率

实例位置:光盘\mingrisoft\03\113

实例说明

在Windows的画图程序中,打开“编辑颜色”窗口时,当鼠标在颜色区域移动时,右方的显示区域和下方的编辑框中会显示相应的颜色和颜色值。本实例实现了该功能,效果如图3.35所示。

图3.35 获取鼠标任意位置的颜色值

技术要点

获取某一点的颜色很容易,只要得到当前鼠标下的设备上下文CDC类就可以了,因为调用CDC类的GetPixel方法可以获取某一点的颜色值。但是,通过颜色值如何获得红、绿、蓝三原色的值呢?Visual C++提供了3个宏,用于获取某一颜色的红、绿、蓝三原色,这3个宏分别如下所示。

(1)GetRValue宏。该宏用于获取指定颜色的红颜色值。语法如下:

BYTE GetRValue(DWORD rgb );

参数说明:

● rgb:标识一个颜色值。

● 返回值:指定颜色的红色值。

(2)GetGValue宏。该宏用于获取指定颜色的绿颜色值。语法如下:

BYTE GetGValue(DWORD rgb );

参数说明:

● rgb:标识一个颜色值。

● 返回值:指定颜色的绿色值。

(3)GetBValue宏。该宏用于获取指定颜色的蓝色值。语法如下:

BYTE GetBValue(DWORD rgb );

参数说明:

● rgb:标识一个颜色值。

● 返回值:指定颜色的蓝色值。

在MFC应用程序中,颜色值通常采用COLORREF类型,在设置颜色值时,可以利用RGB宏将红、绿、蓝三原色组合为一个COLORREF类型颜色值。例如获取红颜色,代码如下:

RGB(255,0,0); //获取红颜色

实现过程

(1)新建一个基于对话框的应用程序。

(2)在对话框中添加静态文本控件、图片控件和编辑框控件。

(3)主要程序代码如下:

        void CFetchColorDlg::OnMouseMove(UINT nFlags, CPoint point)
        {
            CDialog::OnMouseMove(nFlags, point);
            CWnd* temp = ChildWindowFromPoint(point);
            if (temp)
            {
              CDC* m_dc = temp->GetDC();
              COLORREF m_color;
              this->MapWindowPoints(temp,&point,1);
              //获取颜色值
              m_color = m_dc->GetPixel(point);
              int r,g,b;
              r = GetRValue(m_color);
              g = GetGValue(m_color);
              b = GetBValue(m_color);
              //在编辑框中显示颜色值
              CString str;
              str.Format("%i",r);
              m_red.SetWindowText(str);
              str.Format("%i",g);
              m_green.SetWindowText(str);
              str.Format("%i",b);
              m_blue.SetWindowText(str);
              //使用当前颜色填充区域
              CRect m_rect;
              m_test.GetClientRect(m_rect);
              CDC* dc = m_test.GetDC();
              CBrush m_brush(RGB(r,g,b));
              dc->FillRect(m_rect,&m_brush);
            }
        }

举一反三

根据本实例,读者可以:

设计颜色拾取器。

实例114 提取图片中的对象

这是一个可以提高分析能力的实例

实例位置:光盘\mingrisoft\03\114

实例说明

在图像识别领域中,图像提取是最基础也是最重要的一个环节。如果前期图像提取错误,无论识别技术多么高超,最终也不会识别出正确的对象。本实例将实现从图像中提取人物轮廓,效果如图3.36所示。

图3.36 提取图片中的对象

技术要点

要从图像中提取某个对象,首先需要观察提取对象的特征。例如本实例中提取的对象,轮廓是由黑色的像素构成,因此只要将黑色像素提取出来,则对象的轮廓也就描述出来了。设备上下文CDC类提供了GetPixel方法和SetPixel方法,用于获取或设置某个点的颜色。下面详细介绍这两个方法。

(1)GetPixel方法。该方法用于获取某一点的颜色值。语法如下:

          COLORREF GetPixel( int x, int y ) const;
          COLORREF GetPixel( POINT point ) const;

参数说明:

● x、y、point:标识坐标点。

● 返回值:坐标点的颜色值。

(2)SetPixel方法。该方法用于设置某一点的颜色值。语法如下:

          COLORREF SetPixel( int x, int y, COLORREF crColor );
          COLORREF SetPixel( POINT point, COLORREF crColor );

参数说明:

● x、y、point:标识坐标点。

● crColor:标识设置的颜色值。

● 返回值:坐标点实际显示的颜色值。

实现过程

(1)新建一个基于对话框的应用程序。

(2)在对话框中添加图片控件和按钮控件。

(3)主要程序代码如下:

          void CFetchObjectDlg::OnFetch()
          {
              CDC* dc = m_image.GetDC();
              CDC* m_dc = m_demo.GetDC();
              CRect m_rect;
              m_image.GetClientRect(&m_rect);           //获得控件的客户区域
              int x,y;
              for(x=0;x<m_rect.right;x++)               //根据宽度循环
                    for(y=0;y<m_rect.bottom;y++)        //根据高度循环
                    {
                    COLORREF m_color;
                    m_color=dc->GetPixel(x,y);  //获得当前颜色
                    //判断是否为要获取的颜色
                    if ((m_color ==RGB(255,255,255))||(m_color==RGB(0,0,0))|| (m_color == RGB(252,197,30)))
                    {
                      m_dc->SetPixel(x,y,m_color);
                    }
              }
        }

举一反三

根据本实例,读者可以:

开发简易的图像识别程序。

实例115 手写数字识别

本实例是一个提高效率的程序

实例位置:光盘\mingrisoft\03\115

实例说明

如今许多软件都提供了文字识别功能。例如在一些手机的应用软件中,用户能够通过手写文字实现短信发送,其中就涉及了文字的识别技术。本实例实现了手写数字的识别,采用联机字符识别技术,效果如图3.37所示。

图3.37 手写数字识别

技术要点

手写数字识别的难度在于其形状极多。对于规范的手写数字,可以采用模板匹配的方法。但是,由于每个人的字体不尽相同,导致数字或大或小、或胖或瘦,如图3.38所示,采用模板匹配就行不通了。

图3.38 手写数字

在图3.38中,虽然“2”的写法不同,但人眼一下就能识别出它们是“2”。分析原因,这两个字都是向右、向左下、向右的书写顺序。它们的特征在于笔顺相同。本实例就是以数字的笔顺为特征区别手写数字的。

实现过程

(1)新建一个基于对话框的应用程序。

(2)向对话框中添加图片控件和按钮控件。

(3)主要程序代码如下:

        void CRegFigureDlg::OnReg()
        {
            m_curpen = 0;
            if(m_Figure.Direction[0]==down)                //判断1
              if(m_Figure.Direction[1]==none)              //只有一笔记录
              {
                    if ( m_Figure.DotCount == 1)
                    {
                        MessageBox("1");
                        for(int i=0;i<16;i++)               //重新初始化m_Figure
                        {
                            m_Figure.Direction[i]= none;
                        }
                        m_Figure.DotCount = 0;
                        m_Panel.Invalidate();              //更新图片控件
                        return;
                    }
                    }
              if(m_Figure.Direction[0]==right)                 //判断7
                  if (m_Figure.Direction[1]==down)
                      if (m_Figure.Direction[2]==none)
                    {
                      if ( m_Figure.DotCount == 1)
                      {
                            MessageBox("7");
                          for(int i=0;i<16;i++)                 //重新初始化m_Figure
                          {
                                m_Figure.Direction[i]=none;
                          }
                            m_Figure.DotCount=0;
                            m_Panel.Invalidate();              //更新图片控件
                          return;
                      }
                    }
              if(m_Figure.Direction[0]==right)                 //判断2
                  if (m_Figure.Direction[1]==down)
                    {
                      if (m_Figure.DotCount == 1)
                      {
                          if (m_Figure.Direction[2]==left)
                          {
                                if(m_Figure.Direction[3]==right)
                                  if ( m_Figure.Direction[4]==none)
                                  {
                                        MessageBox("2");
                                        for(int i=0;i<16;i++)             //重新初始化m_Figure
                                        {
                                            m_Figure.Direction[i]=none;
                                        }
                                        m_Figure.DotCount=0;
                                        m_Panel.Invalidate();            //更新图片控件
                                        return;
                                  }
                          }
                          else if (m_Figure.Direction[2]==right)
                          {
                                if(m_Figure.Direction[3]==none)
                                {
                                  MessageBox("2");
                                  for(int i=0;i<16;i++)                   //重新初始化m_Figure
                                  {
                                        m_Figure.Direction[i]=none;
                                  }
                                  m_Figure.DotCount = 0;
                                  m_Panel.Invalidate();                  //更新图片控件
                                  return;
                                }
                          }
                      }
                    }
              if(m_Figure.Direction[0]==right)                           //判断3
                  if (m_Figure.Direction[1]==down)
                      if (m_Figure.DotCount==1)
                      {
                          if (m_Figure.Direction[2]==left)
                                if(m_Figure.Direction[3]==right)
                                {
                                  MessageBox("3");
                                }
                          for(int i=0;i<16;i++)                           //重新初始化m_Figure
                          {
                                m_Figure.Direction[i]=none;
                          }
                            m_Figure.DotCount=0;
                            m_Panel.Invalidate();                        //更新图片控件
                          return;
                      }
              if(m_Figure.Direction[0]==down)                            //判断4
                  if (m_Figure.Direction[1]==right)
                      if (m_Figure.DotCount==2)
                      {
                          if(m_Figure.Direction[2]==down)
                            if(m_Figure.Direction[3]==none)
                            {
                                for(int i=0;i<16;i++)                 //重新初始化m_Figure
                                {
                                    m_Figure.Direction[i]= none;
                                }
                                m_Figure.DotCount = 0;
                                m_Panel.Invalidate();                //更新图片控件
                                MessageBox("4");
                                return;
                            }
                    }
            if(m_Figure.Direction[0]==down)                          //判断5
              if (m_Figure.Direction[1]==right)
                    if (m_Figure.DotCount==2)
                        if(m_Figure.Direction[2]==down)
                            if(m_Figure.Direction[3]==left)
                                if(m_Figure.Direction[4]==right)
                                    if (m_Figure.Direction[5]==none)
                                    {
                                        MessageBox("5");
                                        for(int i=0;i<16;i++)            //重新初始化m_Figure
                                        {
                                            m_Figure.Direction[i]= none;
                                        }
                                        m_Figure.DotCount = 0;
                                        m_Panel.Invalidate();           //更新图片控件
                                        return;
                                    }
              if(m_Figure.DotCount==1)                                  //判断6
                    if (m_Figure.Direction[1]==down)
                        if (m_Figure.Direction[2]==right)
                        if(m_Figure.Direction[3]==up)
                            if(m_Figure.Direction[4]==left)
                                    {
                                        MessageBox("6");
                                        for(int i=0;i<16;i++)            //重新初始化m_Figure
                                        {
                                            m_Figure.Direction[i]= none;
                                        }
                                        m_Figure.DotCount = 0;
                                        m_Panel.Invalidate();           //更新图片控件
                                        return;
                                    }
              if(m_Figure.DotCount==1)                                  //判断8
              {
                    if ( m_Figure.Direction[0]==left)
                    {
                        if (m_Figure.Direction[1]==down)
                            if(m_Figure.Direction[2]==left)
                                if(m_Figure.Direction[3]==up)
                                    if(m_Figure.Direction[4]==right)
                                        if(m_Figure.Direction[5]==up)
                                        {
                                            MessageBox("8");
                                            for(int i=0;i<16;i++)        //重新初始化m_Figure
                                            {
                                              m_Figure.Direction[i]= none;
                                            }
                                            m_Figure.DotCount = 0;
                                            m_Panel.Invalidate();       //更新图片控件
                                            return;
                                        }
                    }
                    if(m_Figure.Direction[0]==down)                     //第2种判断数字8的方式
                    {
                        if (m_Figure.Direction[1]==right)
                            if(m_Figure.Direction[2]==down)
                                if(m_Figure.Direction[3]==right)
                                    if(m_Figure.Direction[4]==up)
                                        if(m_Figure.Direction[5]==left)
                                        {
                                            MessageBox("8");
                                            for(int i=0;i<16;i++)        //重新初始化m_Figure
                                            {
                                              m_Figure.Direction[i]= none;
                                        }
                                        m_Figure.DotCount = 0;
                                        m_Panel.Invalidate();               //更新图片控件
                                        return;
                                            }
                      }
                      if(m_Figure.Direction[0]==left)                       //第3种判断数字8的方式
                      {
                          if (m_Figure.Direction[1]==down)
                                if(m_Figure.Direction[2]==right)
                                  if(m_Figure.Direction[3]==down)
                                        if(m_Figure.Direction[4]==left)
                                            if(m_Figure.Direction[5]==up)
                                            {
                                        MessageBox("8");
                                        for(int i=0;i<16;i++)                //重新初始化m_Figure
                                        {
                                          m_Figure.Direction[i]= none;
                                        }
                                        m_Figure.DotCount = 0;
                                        m_Panel.Invalidate();               //更新图片控件
                                        return;
                                            }
                      }
                    }
                  if(m_Figure.DotCount==1)                                  //判断9
                    {
                      if ( m_Figure.Direction[0]==left)
                      {
                          if (m_Figure.Direction[1]==down )
                                if(m_Figure.Direction[2]==right)
                                {
                                  MessageBox("9");
                                  for(int i=0;i<16;i++)                      //重新初始化m_Figure
                                  {
                                        m_Figure.Direction[i]=none;
                                  }
                                  m_Figure.DotCount = 0;
                                  m_Panel.Invalidate();                     //更新图片控件
                                  return;
                                }
                      }
                      if(m_Figure.Direction[0]==right)                      //第2种判断9的方式
                          if ( m_Figure.Direction[1]==up)
                                if(m_Figure.Direction[2]==left)
                                  if ( m_Figure.Direction[3]==down)
                                  {
                                        MessageBox("9");
                                        for(int i=0;i<16;i++)                //重新初始化m_Figure
                                        {
                                            m_Figure.Direction[i]=none;
                                        }
                                        m_Figure.DotCount=0;
                                        m_Panel.Invalidate();               //更新图片控件
                                        return;
                                  }
                    }
              for(int i=0;i<16;i++)                                          //如果没有匹配的模板重新初始化m_Figure
              {
                    m_Figure.Direction[i]=none;
              }
              m_Figure.DotCount = 0;
              m_Panel.Invalidate();                                         //更新图片控件
            }

举一反三

根据本实例,读者可以:

实现手写汉字的识别。