3.4 图片颜色转换

实例104 图像的锐化处理

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

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

实例说明

图像的锐化处理能够使图像的主体轮廓更加清晰,因此在图像处理和图像识别程序中应用得非常广泛。本实例演示了图像的锐化效果,如图3.23、图3.24所示。

图3.23 锐化前的效果

图3.24 锐化后的效果

技术要点

本实例中实现图像的锐化处理是通过调整像素点的颜色来实现的。在介绍“提取图片中的对象”实例时,讲解了CDC类的GetPixel方法和SetPixel方法可以获取和设置某一点的颜色,本实例通过这两个方法实现图像的锐化处理。

实现过程

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

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

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

        void CImagesharpDlg::OnSharp()
        {
            CDC * m_dc;
            CRect m_rect;
            m_image1.GetClientRect(m_rect);          //获得控件的客户区域
            m_dc=m_image1.GetDC();              //获得设备上下文
            int r1,g1,b1,r2,g2,b2;
            for(int i=1;i<m_rect.right+1;i++)          //根据宽度循环
              for(int j=1;j<m_rect.bottom+1;j++)    //根据高度循环
              {
              //锐化处理
              COLORREF color = m_dc->GetPixel(i,j);
              COLORREF nextcolor = m_dc->GetPixel(i-1,j-1);
              r1 = (color & 0xFF);
              g1 = (int)(color & 62580) / 256;
              b1 = (int)(color & 0xFF0000) / 65536;
              r2 = (nextcolor & 0xFF);
              g2 = (int)(nextcolor & 62580) / 256;
              b2 = (int)(nextcolor & 0xFF0000) / 65536;
              r1+= (r1- r2) /2;
              g1+=  (g1-g2)/2;
              b1+=  (b1-b2)/2;
              if ( r1 > 255)
                      r1 = 255;
                  if ( r1 < 0 )
                          r1 = 0;
                  if ( b1 > 255)
                          b1 = 255;
                  if (b1 < 0)
                          b1 = 0;
                  if( g1 > 255)
                          g1 = 255;
                  if ( g1 < 0 )
                          g1 = 0;
                  m_dc->SetPixel(i,j,RGB(r1,g1,b1));
              }
          }

举一反三

根据本实例,读者可以:

设计渐变效果程序启动界面。

实例105 图片反色处理

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

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

实例说明

图片反色处理是将图片中的像素值取反。例如,原来的白色像素点取反后会成为黑色的像素点。图片的反色处理是一个逆运算过程,即对一幅图片进行两次取反处理,还应是原来的图片效果。本实例实现了图片的反色处理。反色处理前后的效果如图3.25、图3.26所示。

图3.25 处理前的效果图

图3.26 处理后的效果图

技术要点

要实现图像的反色处理可以有多种方法。例如可以获取图片中每个像素点的颜色值,然后对颜色值进行取反,代码如下:

          CDC* pDC = m_image.GetDC();
          CRect m_rect;
          m_image.GetClientRect(m_rect);
          BYTE r,g,b;
          for (int i=1; i<]m_rect.Width();i++)
              for (int j=1;j<m_rect.Height();j++)
              {
              COLORREF clr= pDC->GetPixel(i,j);
              r = GetRValue(clr);
              g = GetGValue(clr);
              b = GetBValue(clr);
              r = abs(255-r);
              g = abs(255-g);
              b = abs(255-b);
              pDC->SetPixel(i,j,RGB(r,g,b));
              }

此外,还有一种更简单的方法,就是调用CDC类的InvertRgn方法,该方法将指定区域的颜色取反,语法如下:

BOOL InvertRgn( CRgn* pRgn );

参数说明:

● pRgn:是一个区域对象指针。

与第一种方法相比,该方法计算速度更快,而且也相对简单,本实例也是采用该方法实现的,关键代码如下:

        CDC* pDC = m_image.GetDC();
        CRect m_rect;
        m_image.GetClientRect(m_rect);
        CRgn m_rgn;
        m_rgn.CreateRectRgn(m_rect.left,m_rect.top,m_rect.right,m_rect.bottom);
        pDC->InvertRgn(&m_rgn);

实现过程

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

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

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

        void CReverseColorDlg::OnOK()
        {
            CDC*pDC=m_image.GetDC();             //获得设备上下文
            CRect m_rect;
            m_image.GetClientRect(m_rect);       //获得控件客户区域
            CRgn m_rgn;
            m_rgn.CreateRectRgn(m_rect.left,m_rect.top,m_rect.right,m_rect.bottom);//设置区域
            pDC->InvertRgn(&m_rgn);              //颜色取反处理
        }

举一反三

根据本实例,读者可以:

设计具有透明效果的图像。

实例106 图像的灰度化转换

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

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

实例说明

在图像识别领域,经常涉及图像的灰度化转换,即将彩色图像转换为黑白图像。因为黑白图像更容易进行运算。本实例将实现图像的灰度化转换,效果如图3.27、图3.28所示。

图3.27 转换前图像

图3.28 转换后图像

技术要点

实现图像的灰度化转换没有一定的标准,通常根据图片中像素的RGB分量以及它们的权重来获取。本实例中RGB分量的权重分别为0.38、0.49、0.1。在其他应用中,用户可以根据实际情况设置不同的权重。在图像的灰度化过程中,首先需要获取像素点的红、绿、蓝分量值,然后将其乘以相应的分量,最后重新设置像素的颜色,例如下面的代码:

          m_color = pDC->GetPixel(i,j);
          r = GetRValue(m_color);
          g = GetGValue(m_color);
          b = GetRValue(m_color);
          m_gray = (0.38*r+0.49*g+0.1*b);
          m_color = RGB(m_gray,m_gray,m_gray);
          pDC->SetPixel(i,j,m_color);

实现过程

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

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

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

          void CSingleImageDlg::OnOK()
          {
              CDC*pDC=m_image.GetDC();             //获得设备上下文
              CRect m_rect ;
              m_image.GetClientRect(m_rect);       //获得控件区域
              COLORREF m_color;
              DWORD m_gray;
              BYTE r,g,b;
              for (int i = 0; i<m_rect.right;i++)
                for (int j = 0;j<m_rect.bottom;j++)
                {
                  m_color=pDC->GetPixel(i,j);      //获得颜色
                  r = GetRValue(m_color);
                  g = GetGValue(m_color);
                  b = GetRValue(m_color);
                  m_gray=(0.38*r+0.49*g+0.1*b); //设置灰度颜色值
                  m_color = RGB(m_gray,m_gray,m_gray);
                  pDC->SetPixel(i,j,m_color);      //用灰度颜色画点
                }
          }

举一反三

根据本实例,读者可以:

利用抠图技术设计特殊形状的窗体。

实例107 显示JPG图片

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

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

实例说明

用过Delphi的读者知道,使用VCL提供的TImage可以方便地显示JPG图像。在Visual C++中,MFC类库却没有提供相应的控件显示JPG图像。本实例实现了JPG图片的显示,效果如图3.29所示。

图3.29 显示JPG图片

技术要点

JPG图像采用压缩形式存储,存储结构比较复杂。为了简单化,可以采用流的形式读取JPG文件。首先利用CFile对象读取JPG文件,获取JPG文件的长度,然后利用GlobalAlloc函数在堆中划分一个和文件大小相同的区域,接着调用GlobalLock函数锁定该区域,返回一个指向堆的指针,将文件数据写入该区域,最后调用CreateStreamOnHGlobal函数在堆中创建流对象,调用OleLoadPicture函数根据流对象创建图像对象IPicture,调用IPicture的Render方法将图像显示在指定的画布上。

在实现JPG图像的过程中,需要用到多个函数,下面逐一进行介绍。

(1)GlobalAlloc函数。该函数用于在堆上分配指定大小的空间。语法如下:

HGLOBAL GlobalAlloc(UINT uFlags, DWORD dwBytes);

参数说明:

● uFlags:标识如何分配空间,为GMEM_FIXED时表示空间是固定的;为GMEM_MOVEABLE时表示空间可以移动。

● dwBytes:标识分配空间的大小。

● 返回值:分配的空间对象句柄。

(2)GlobalLock函数。该函数用于锁定一个全局内存对象,并返回一个指向内存的指针。语法如下:

LPVOID GlobalLock(HGLOBAL hMem );

参数说明:

● hMem:全局内存对象句柄,通常是GlobalAlloc函数的返回值。

● 返回值:函数返回指向全局内存的指针。

(3)CreateStreamOnHGlobal函数。该函数用于创建一个存储在堆中的流对象。语法如下:

WINOLEAPI CreateStreamOnHGlobal(HGLOBAL hGlobal, BOOL fDeleteOnRelease, LPSTREAM * ppstm );

参数说明:

● hGlobal:标识全局内存对象句柄。

● fDeleteOnRelease:确定在流对象释放时是否释放全局内存空间。

● ppstm:标识Istream接口指针。

(4)OleLoadPicture函数。该函数根据流对象的内容创建一个图像对象。语法如下:

        STDAPI OleLoadPicture( IStream * pStream,LONG lSize, BOOL fRunmode,REFIID riid,
        VOID ppvObj );

参数说明:

● pStream:标识流对象指针。

● lSize:标识从流中读取数据的数量。

● fRunmode:确定是否采用与图像初始属性相反的属性值。

● riid:确定接口指针的类型。

● ppvObj:标识与riid对应的图像对象指针。

实现过程

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

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

(3)处理按钮的单击事件,加载并显示JPG图像,代码如下:

void CShowJPGDlg::OnOK()

        {
            CFileDialog m_dlg(TRUE,"JPG",NULL,NULL,"JPG(*.jpg)|*.JPG|gif|*.gif",this);
            if (m_dlg.DoModal()==IDOK)
            {
              CString m_filename =m_dlg.GetPathName();
              m_dir.SetWindowText(m_filename);
              CFile m_file(m_filename,CFile::modeRead );
              //获取文件长度
              DWORD m_filelen = m_file.GetLength();
              //在堆上分配空间
              HGLOBAL m_hglobal = GlobalAlloc(GMEM_MOVEABLE,m_filelen);
              LPVOID pvdata = NULL;
              //锁定堆空间,获取指向堆空间的指针
              pvdata = GlobalLock(m_hglobal);
              //将文件数据读取到堆中
              m_file.ReadHuge(pvdata,m_filelen);
              IStream*  m_stream;
              GlobalUnlock(m_hglobal);
              //在堆中创建流对象
              CreateStreamOnHGlobal(m_hglobal,TRUE,&m_stream);
              //利用流加载图像
              OleLoadPicture(m_stream,m_filelen,TRUE,IID_IPicture,(LPVOID*)&m_picture);
              m_picture->get_Width(&m_width);
              m_picture->get_Height(&m_height);
              CDC* dc = GetDC();
              m_IsShow = TRUE;
              CRect rect;
              GetClientRect(rect);
              SetScrollRange(SB_VERT,0,(int)(m_height/26.45)-rect.Height());
              SetScrollRange(SB_HORZ,0,(int)(m_width/26.45)-rect.Width());
              m_picture->Render(*dc,1,50,(int)(m_width/26.45),(int)(m_height/26.45),
                0,m_height,m_width,-m_height,NULL);
              }
            }

举一反三

根据本实例,读者可以:

浏览GIF动画。