数字图像处理

图像的基本操作

  (2007-04-04 09:32:13)
  分类: VC++实现
Visual C++6.0开发灰度位图处理

图像处理技术已经渗透到人类生活的各个领域并得到越来越多的应用,图像处理所涉及的图像格式有很多种,如TIF、JEMP、BMP等等,工程应用中经常要处理256级的灰度BMP图像,如通过黑白采集卡采集得到的图像。BMP灰度图像作为Windows环境下主要的图像格式之一,以其格式简单,适应性强而倍受欢迎。在进行图像处理时,操作图像中的像素值就要得到图像阵列;经过处理后的图像的像素值存储起来;显示图像时要正确实现调色板,结合这些问题,文章针对性的给出了操作灰度BMP图像时的部分函数实现代码及注释。

  一、 BMP位图操作

  BMP位图包括位图文件头结构BITMAPFILEHEADER、位图信息头结构BITMAPINFOHEADER、位图颜色表RGBQUAD和位图像素数据四部分。处理位图时要根据文件的这些结构得到位图文件大小、位图的宽、高、实现调色板、得到位图像素值等等。对于256级灰度图像每个像素用8bit表示颜色的索引值,这里要注意的一点是在BMP位图中,位图的每行像素值要填充到一个四字节边界,即位图每行所占的存储长度为四字节的倍数,不足时将多余位用0填充。

  在处理图像应用程序的文档类(CdibDoc.h)中声明如下宏及公有变量:

  #define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)//计算图像每行象素所占的字节数目

  HANDLE m_hDIB;//存放位图数据的句柄

  CPalette* m_palDIB;//指向调色板Cpalette类的指针

  CSize m_sizeDoc; file://初始化视图的尺寸

  1、 读取灰度BMP位图

  根据BMP位图文件的结构,操作BMP位图文件读入数据,重载了文挡类的OnOpenDocument函数如下:

BOOL CDibDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
 CFile file;
 CFileException fe;
 if (!file.Open(lpszPathName, CFile::modeRead | CFile::shareDenyWrite, &fe))
 {
  AfxMessageBox("文件打不开");
  return FALSE;
  }//打开文件
 DeleteContents();//删除文挡
 BeginWaitCursor();
 BITMAPFILEHEADER bmfHeader;//定义位图文件头结构
 DWORD dwBitsSize;
 HANDLE hDIB;
 LPSTR pDIB;
 BITMAPINFOHEADER *bmhdr;//指向位图信息头结构的指针
 dwBitsSize = file.GetLength();//得到文件长度
 if (file.Read((LPSTR)&bmfHeader, sizeof(bmfHeader)) !=
             sizeof(bmfHeader))
  return FALSE;
  if (bmfHeader.bfType != 0x4d42) file://检查是否为BMP文件
   return FALSE;
   hDIB=(HANDLE) ::GlobalAlloc(GMEM_MOVEABLE |
                 GMEM_ZEROINIT, dwBitsSize);
   file://申请缓冲区
   if (hDIB == 0)
   {
    return FALSE;
   }
   pDIB = (LPSTR) ::GlobalLock((HGLOBAL)hDIB);
   file://得到申请的缓冲区的指针
   if (file.ReadHuge(pDIB, dwBitsSize - sizeof(BITMAPFILEHEADER)) !=
     dwBitsSize - sizeof(BITMAPFILEHEADER) )
   {
    ::GlobalUnlock((HGLOBAL)hDIB);
    hDIB=NULL;
    return FALSE;
    }//读数据,包括位图信息、位图颜色表、图像像素的灰度值
   bmhdr=(BITMAPINFOHEADER*)pDIB;//为指向位图信息头结构的指针付值
   ::GlobalUnlock((HGLOBAL)hDIB);
   if ((*bmhdr).biBitCount!=8) file://验证是否为8bit位图
    return FALSE;
    m_hDIB=hDIB;
    InitDIBData();
    file://自定义函数,根据读入的数据得到位图的宽、高、颜色表
    file:// 来得到初始化视的尺寸、生成调色板
    EndWaitCursor();
    SetPathName(lpszPathName);//设置存储路径
    SetModifiedFlag(FALSE); // 设置文件修改标志为FALSE
    return TRUE;
   }

2、 灰度位图数据的存储

  为了将图像处理后所得到的像素值保存起来,重载了文档类的OnSaveDocument函数,其具体实现如下:

BOOL CDibDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
 CFile file;
 CFileException fe;
 BITMAPFILEHEADER bmfHdr; // 位图文件头结构
 LPBITMAPINFOHEADER lpBI; file://指向位图信息结构的指针
 DWORD dwDIBSize;
 if (!file.Open(lpszPathName, CFile::modeCreate |
   CFile::modeReadWrite | CFile::shareExclusive, &fe))
 {
  AfxMessageBox("文件打不开");
 }//打开文件
 BOOL bSuccess = FALSE;
 BeginWaitCursor();
 lpBI = (LPBITMAPINFOHEADER) ::GlobalLock((HGLOBAL) m_hDIB);
 if (lpBI == NULL)
  return FALSE;
  dwDIBSize = *(LPDWORD)lpBI + 256*sizeof(RGBQUAD);
   // Partial Calculation
  DWORD dwBmBitsSize;//BMP文件信息结构所占的字节数
  dwBmBitsSize=WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount))  *lpBI->biHeight;// 存储时位图所有像素所占的总字节数
  dwDIBSize += dwBmBitsSize;
  lpBI->biSizeImage = dwBmBitsSize; // 位图所有像素所占的总字节数
  file://以下五句为文件头结构填充值
  bmfHdr.bfType =0x4d42; // 文件为"BMP"类型
  bmfHdr.bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER);//文件总长度
  bmfHdr.bfReserved1 = 0;
  bmfHdr.bfReserved2 = 0;
  bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + lpBI->biSize
            + 256*sizeof(RGBQUAD);
  file://位图数据距问件头的偏移量
  file.Write((LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER));//写文件头
  file.WriteHuge(lpBI, dwDIBSize);
  file://将位图信息(信息头结构、颜色表、像素数据)写入文件
  ::GlobalUnlock((HGLOBAL) m_hDIB);
  EndWaitCursor();
  SetModifiedFlag(FALSE); // back to unmodified
  return TRUE;
 }

二、 调色板的操作

  灰度图像要正确显示,必须实现逻辑调色板和系统调色板,通过在主框架类中处理Windows定义的消息WM_QUERYNEWPALETTE 、WM_PALETTECHANGED及视图类中处理自定义消息WM_DOREALIZE(该消息在主框架窗口定义如下:#define WM_REALIZEPAL (WM_USER+100))来实现调色板的操作。

void CMainFrame::OnPaletteChanged(CWnd* pFocusWnd)
{ file://总实现活动视的调色板
 CMDIFrameWnd::OnPaletteChanged(pFocusWnd);
 CMDIChildWnd* pMDIChildWnd = MDIGetActive();
 if (pMDIChildWnd == NULL)
  return
  CView* pView = pMDIChildWnd->GetActiveView();
  ASSERT(pView != NULL);
  SendMessageToDescendants(WM_DOREALIZE, (WPARAM)pView->m_hWnd);
  file://通知所有子窗口系统调色板已改变
 }
BOOL CMainFrame::OnQueryNewPalette()//提供实现系统调色板的机会
 {
  // 实现活动视的调色板
  CMDIChildWnd* pMDIChildWnd = MDIGetActive();
  if (pMDIChildWnd == NULL)
   return FALSE; // no active MDI child frame (no new palette)
   CView* pView = pMDIChildWnd->GetActiveView();
   ASSERT(pView != NULL);
   file://通知活动视图实现系统调色板
   pView->SendMessage(WM_DOREALIZE, (WPARAM)pView->m_hWnd);
   return TRUE;
 }
 LRESULT CDibView::OnDoRealize(WPARAM wParam, LPARAM)//实现系统调色板
 {
  ASSERT(wParam != NULL);
  CDibDoc* pDoc = GetDocument();
  if (pDoc->m_hDIB == NULL)
   return 0L; // must be a new document
   CPalette* pPal = pDoc->m_palDIB;
   file://调色板的颜色表数据在InitDIBData()函数中实现
   if (pPal != NULL)
   {
    CMainFrame* pAppFrame = (CMainFrame*) AfxGetApp()->m_pMainWnd;
    ASSERT_KINDOF(CMainFrame, pAppFrame);
    CClientDC appDC(pAppFrame);
    CPalette* oldPalette = appDC.SelectPalette(pPal, ((HWND)wParam) != m_hWnd);
    file://只有活动视才可以设为"FALSE",
    // 即根据活动视的调色板设为"前景"调色板
    if (oldPalette != NULL)
    {
     UINT nColorsChanged = appDC.RealizePalette();//实现系统调色板
     if (nColorsChanged > 0)
      pDoc->UpdateAllViews(NULL);//更新视图
      appDC.SelectPalette(oldPalette, TRUE);
      file://将原系统调色板置为逻辑调色板
     }
    else
    {
     TRACE0("\tSelectPalette failed in
          CDibView::OnPaletteChanged\n");
     }
    }

  注:在调用API函数显示位图时,不要忘记设置逻辑调色板,即"背景"调色板,否则位图将无法正确显示。

 三、图像的数字化处理

  通过以上读文件的操作,已经得到图像数据,由于得到的数据包括多余信息,所以在进行数字图像处理时要进一步删除多余信息,只对位图的像素进行操作,以基于模板的高通滤波为例来讲述数字图像处理的实现:

void CDibView::OnMENUHighPass()
{ HANDLE data1handle;
 LPBITMAPINFOHEADER lpBi;
 CDibDoc *pDoc=GetDocument();
 HDIB hdib; unsigned char *hData; unsigned char *data;
 hdib=pDoc->GetHDIB();
 BeginWaitCursor();
 lpBi=(LPBITMAPINFOHEADER)GlobalLock((HGLOBAL)hdib);
 hData=(unsigned char*)FindDIBBits((LPSTR)lpBi);
 pDoc->SetModifiedFlag(TRUE);
 data1handle=GlobalAlloc(GMEM_SHARE,WIDTHBYTES(lpBi->biWidth*8)*lpBi->biHeight);
 data=(unsigned char*)GlobalLock((HGLOBAL)data1handle);
 AfxGetApp()->BeginWaitCursor();
 int i,j,s,t,ms=1;
 int sum=0,sumw=0;
 int mask[3][3]={{-1,-1,-1},{-1,9,-1},{-1,-1,-1}};
 for(i=0; ibiHeight; i++)
  for(j=0; jbiWidth; j++)
   {
    sumw=0; sum=0;
    for(s=(-ms); s<=ms; s++)
     for(t=(-ms); t<=ms; t++)
      if(((i+s)>=0) && ((j+t)>=0) && ((i+s)biHeight) && ((j+t)biWidth))
      {
       sumw += mask[1+s][1+t];
      sum+=*(hData+(i+s)*WIDTHBYTES(lpBi->biWidth*8)+(j+t))*mask[1+s][1+t];
}
     if(sumw==0) sumw=1; sum/=sumw;
     if(sum>255)sum=255;
     if(sum<0)sum=0;
       *(data+i*WIDTHBYTES(lpBi->biWidth*8)+j)=sum;
      }
     for( j=0; jbiHeight; j++)
      for( i=0; ibiWidth; i++)
*(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)=*(data+i*WIDTHBYTES(lpBi->biWidth*8)+j);
      AfxGetApp()->EndWaitCursor();
      GlobalUnlock((HGLOBAL)hdib);
      GlobalUnlock(data1handle);
      EndWaitCursor();
     Invalidate(TRUE);
 }

四、图像的基本操作处理

  1、图像平移

  图像平移只是改变图像在屏幕上的位置,图像本身并不发生变化。假设原图像区域左上角坐标为(x0, y0),右下角坐标为(x1, y1),将图像分别沿x和y轴平移dx和dy,则新图像的左上角坐标为(x0+dx, y0+dy),右下角坐标为(x1+dx, y1+dy)。坐标平移变换公式为:

  x1 = x + dx

  y1 = y + dy

  在屏幕上实现图像的移动分为四个步骤:

  ⑴ 读原图像到缓冲区;

  ⑵ 擦除视图上原图像;

  ⑶ 计算平移后的新坐标。

  ⑷ 利用API函数::StretchDIBits()在新的左上角坐标位置处重新显示原图像。

  其中,擦除原图像的方法与图形变换中擦除原图形的方法一致,在实现中仍采用XOR异或方式画图擦除原图像。对于新坐标值的计算还需要考虑边界情况,不要在图像平移后超出允许的屏幕范围。

  2、图像颠倒

  图像颠倒是指把定义好的图像区域上下翻转地显示在屏幕上。分析图像颠倒的过程,可发现每行的图像信息都保持不变,而只是改变了行的顺序,将第一行与最后的第n行相互交换,第二行与第n - 1行交换……,依此类推,从而实现了图像的颠倒。只需采用按行交换的方式,即可方便地修改缓冲区内容,实现图像的颠倒。基本步骤如下:

  (1)将原图像读入缓冲区,并擦除原图像;

  (2) 计算图像的高度,即行数height;计算图像宽度width;根据宽度、高度生成新缓冲区;

  (3)把第一行与最末行交换,第2行与第n-1行交换……,依此类推,直至全部交换完毕。既原图中的(x、y)点,在新生成的图象中对应为x1=x,y1=height-1-y。把原图中的象素值读入新缓冲区的(x1,y1)点处。

  (4)把交换后的图像缓冲区内容重新显示在屏幕上。

  3、图像镜像变换

  镜像变换是指将指定区域的图像左右翻转地显示在屏幕。分析镜像变换过程可以发现:每行图像信息的处理方式是相同的,而且行顺序不发生变化,只是每一行的像素信息按从左到右的顺序进行了左右颠倒,从而实现了镜像变换。因此,采用按行逐点变换的方式实现图像的镜像。

  给出原图中的任意点(x, y)镜像变换后的新坐标(x1, y1)的坐标变换公式:

  x1 = width-x-1

  y1 = y

  根据以上公式,对各个像素点计算新坐标后,把原图中的象素值读入新缓冲区的(x1,y1)点处。

  4、图像任意角度的旋转

  图像旋转是指把定义的图像绕某一点以逆时针或顺时针方向旋转一定的角度,通常是指绕图像的中心以逆时针方向旋转。

  首先根据旋转的角度、图象对角线的长度计算旋转后的图像的最大宽度、高度,根据旋转后图象最大的宽度、高度生成新的缓冲区,假设图像的左上角为(left, top),右下角为(right, bottom),则图像上任意点(x, y)绕其中心(xcenter, ycenter)逆时针旋转angle角度后,新的坐标位置(x1, y1)的计算公式为:

  xcenter = (width+1)/2+left;

  ycenter = (height+1)/2+top;

  x1 = (x-xcenter) cosθ- (y - ycenter) sinθ+xcenter;

  y1 = (x-xcenter) sinθ+ (y- ycenter) cosθ+ ycenter;

  与图像的镜像变换相类似,把原图中的象素值读入新缓冲区的(x1,y1)点处。注意在新缓冲区中与原图没有对应的象素点的值用白色代替。

  五、小结

  笔者开发的该图像处理程序在Windows98环境下编译通过,本文主要讲述了8bit灰度图像的处理,读者可以本文的基础上开发自己的针对二值、真彩色格式的图像处理系统。

用VC++实现图像检索技术
一. 理论和方法介绍

   a) 采用颜色检索方法的目的:

  对多媒体数据的检索,早期的方法是用文本将多媒体数据进行标识,这显然不是基于多媒体信息本身内容的检索,对多媒体数据中包含的信息是一中及大的浪费;

  基于内容的检索是多媒体数据库的关键技术,如何实现这块技术,是值得商榷的,而最好的方法是使用无需领域知识的检索方法,因此,基于颜色的方法就是实现的关键;

  本文介绍了颜色直方图和颜色对方法在基于内容检索时的实现思路和理论;

  其实颜色直方图简单来说,就是统计图像中具有某一特定颜色的象素点数目而形成的各颜色的直方图表示,不同的直方图代表不同图片的特征。

   b) 利用颜色直方图进行检索:

  该方法也可以应用于视频数据库的查询中,有以下三种方式:

  (1)指明颜色组成--该法需要用户对图像中的颜色非常敏感,而且使用起来也不方便,检索的查准率和查全率并不高,因此文章中并未介绍该法的实现思路

  (2)指明一幅示例图像--通过与用户确定的图像的颜色直方图的相似性匹配得到查询结果,这是文章介绍的两种方法的根本

  (3)指明图像中一个子图--分割图像为各个小块,然后利用选择小块来确定图像中感兴趣的对象的轮廓,通过建立更复杂的颜色关系(如颜色对方法)来查询图像,该方法是文章的重心所在

   c) 颜色直方图实现思路的介绍:

  两图片是否相似可以采用欧氏距离来描述:

   Ed=(G,S)=  图像的基本操作(Ed越小相似度就越大)
  检索后,全图直方图的相似度的定量度量可以用如下公式表示:

   Sim(G,S)= 图像的基本操作
   (N为颜色级数,Sim越靠近1两幅图片越相似)

  可以对上面2中的公式加改进对某些相对重要的颜色乘上一个权重,就可以做寻找某一前景或组合的查询。

  全图的颜色直方图算法过于简单,因此带来很多问题,如:可能会有两幅根本不同的图像具有完全一样的颜色直方图,不反映颜色位置信息,这样导致查准率和查全率都不高,因此问文章提出了一个改进,即将图像进行了分割,形成若干子块,这样就提供了一定程度的位置信息,而且可以对含用户感兴趣的子块加大权重,提高检索的查询智能性和查准查全率,相应的公式有,子块Gij与Sij的相似性度量为:

    图像的基本操作
  (P为所选颜色空间的样点数)

  再引入子块权重Wij,选取L个最大的Sim值作Simk(Gk,Sk),就有:

    图像的基本操作
(Wk 的选取应根据图像的特点决定,可以使图像中间或用户指定的区域权重大,以反映图像的位置信息)

本文来自:http://blog.sina.com.cn/s/blog_4b665fc1010008sd.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值