登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

图像处理 视频分析 机器视觉 模式识别

方向比努力更重要

 
 
 

日志

 
 
关于我

河北软件开发项目,电子警察卡口项目,公安天网项目,媒体流处理,数字图像处理。媒体服务器 RTSP、图像处理、车牌识别……DCT变换,H.264压缩

详解用VC实现bmp位图的打开  

2009-12-18 13:59:17|  分类: DC|Gdi|Gdi+ |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

我最近在学VC++数字图像处理,作为一个初学者,万里长征的第一步当然是打开一幅图像,这几天一直在看怎么实现这一功能,虽说简单,但是如果这一步不能做到,那么下面也就无法进行了,所以我总结了一下这个过程,写出来供大家参考。也希望大家多多批评啊。

这里我就不想介绍关于位图的理论内容了,只是写一下实现的部分。

0.准备工作

创建一个SDI,工程名Test,“隐藏工具栏”和“打印和打印预览”取消了,不用那么复杂,简单点就行

详解用VC实现bmp位图的打开 - fengqing888 - Forever Ralf_Volcano

1.创建菜单

创建两个菜单:

Caption: 打开       ID: ID_FILE_OPEN

Caption: 显示原图   ID: IDM_YUANTU

详解用VC实现bmp位图的打开 - fengqing888 - Forever Ralf_Volcano

2.对打开菜单进行响应

右键打开菜单,建立类向导,在CTestDoc类中,进行COMMAND响应,生成OnFileOpen函数,代码如下:

void CTestDoc::OnFileOpen()

{

    // TODO: Add your command handler code here

    CFileDialog fileDlg(TRUE);//创建一个CfileDialog类对象fileDlg,第一个参数TRUE为打开对话框,若为FALSE,则为另存为

    fileDlg.m_ofn.lpstrTitle="图片打开对话框";//设置打开对话框的标题

    fileDlg.m_ofn.lpstrFilter="BMP Files(*.bmp)\0*.bmp\0\0";//设置打开的文件类型

    if(IDOK==fileDlg.DoModal ())//这个语句有两层意义,第一是dlg.DoModal()作用是弹出CPortDlg对话框,第二层是dlg.DoModal()==IDOK是你点击了对话框上的OK按钮就是说你同时做了上述两件事时就执行if语句后面的程序。

    filename.Format ("%s",fileDlg.GetPathName());//将完整路径通过Format函数以字符串类型存入filename中

★CDib.LoadFile(filename); //注意这里CDib不是类,而是CDib类的对象,对象名称也为CDib,千万不要弄混,类不能直接调用成员函数,而类的对象则可以调用

}

其中,注意在CTestDoc类中,添加一个变量filename,Cstring类型,该变量作用是保存所指定的文件的文件完整路径。

注意最后一行★。这一句代码用来实现文件的读取(不含显示)。这里用到了CDib类的对象CDib,因此需要在CTestDoc.h中添加CDib类的头文件#include “Dib.h”,并且在CTestDoc类中添加这个对象,类型CDib,变量名称CDib。然后执行CDib类的成员函数LoadFile来读取文件。

详解用VC实现bmp位图的打开 - fengqing888 - Forever Ralf_Volcano

3.CDib类的操作

这是一个处理DIB位图的专用类,封装了一些相关的函数与变量,基类选为CObject类。

在Dib.h中做如下声明:

class CDib : public CObject

{

public:

    RGBQUAD* m_pRGB;

    BYTE* m_pData;

    UINT m_numberOfColors;

BOOL m_valid;

    BITMAPFILEHEADER bitmapFileHeader;//定义了一个文件头结构体的对象

    BITMAPINFOHEADER* m_pBitmapInfoHeader;//定义了一个指向信息头的结构体指针

    BITMAPINFO* m_pBitmapInfo;//定义了一个结构体指针,BITMAPINFO是一个包含有信息头,和调色板的结构体。

    BYTE* pDib;

DWORD size;

char m_fileName[256];//定义字符数组用来存放文件路径

public:

UINT GetNumberOfColors();

UINT GetHeight();

UINT GetWidth();

DWORD GetSize();

void LoadFile(const char* dibFileName);

CDib();

virtual ~CDib();

};

CDib.cpp代码如下:

CDib::CDib()

{

    size=0;//构造函数初始化size

}

CDib::~CDib()

{

GlobalFreePtr(m_pBitmapInfo); //详见说明(1)

}

void CDib::LoadFile(const char* dibFileName)

{

    strcpy(m_fileName,dibFileName);//将路径名称拷贝到m_fileName之中

    CFile dibFile(m_fileName, CFile::modeRead);//创建CFile类对象,只读方式

    dibFile.Read((void*)&bitmapFileHeader,sizeof(BITMAPFILEHEADER));//读取文件头的内容

    if (bitmapFileHeader.bfType == 0x4d42)//判断是否为bmp格式,单步调试你会发现,此时的bfType值并非0x4d42,而是19778.注意,这是十进制,只要转换成十六进制即为0x4d42

    {

        DWORD fileLength = dibFile.GetLength();//读取文件的大小,你可以试试跟踪此值来看看它是否和你要打开的图片大小一致

         size = fileLength -sizeof(BITMAPFILEHEADER);//文件大小-文件头结构体的大小,此时你会发现,文件头的大小的确是14字节

         pDib =(BYTE*)GlobalAllocPtr(GMEM_MOVEABLE, size);//详见说明(2)

        dibFile.Read((void*)pDib, size);//通过读取,把读出的数据存入刚才分配的内存之中

        dibFile.Close();//文件操作完成之后关闭文件

        m_pBitmapInfo = (BITMAPINFO*) pDib;//BITMAPINFO结构体指针指向该内存

        m_pBitmapInfoHeader = (BITMAPINFOHEADER*) pDib;//信息头指向该内存

        m_pRGB = (RGBQUAD*)(pDib +

    m_pBitmapInfoHeader->biSize);//调色板指针指向该内存的调色板部分。因为pDib原本指向信息头,偏移40字节(信息头结构体的大小)之后便到了调色板部分,因此用加法来实现指针的偏移

        int m_numberOfColors = GetNumberOfColors();//调用GetNumberOfColors函数来得到颜色数

        if (m_pBitmapInfoHeader->biClrUsed == 0)

            m_pBitmapInfoHeader->biClrUsed =

       m_numberOfColors;//把颜色数赋予biClrUsed之中

        DWORD colorTableSize = m_numberOfColors *

            sizeof(RGBQUAD);//用每个调色板结构体大小乘以颜色数量,得到调色板的大小

        m_pData = pDib + m_pBitmapInfoHeader->biSize

            + colorTableSize;//这时候代表把m_pData指针指向实际图像数据了

   if (m_pRGB == (RGBQUAD*)m_pData) // 如果调色板指针位置和实际图像位置指针指向位置相同,那就代表没有调色板

    m_pRGB = NULL;//指针赋予空

        m_pBitmapInfoHeader->biSizeImage = GetSize();//赋予实际位图的大小

   m_valid = TRUE;

    }   

    else//如果不是bmp位图则失败

    {

        m_valid = FALSE;

        AfxMessageBox("This isn't a bitmap file!");

    }

}

DWORD CDib::GetSize()

{

    if (m_pBitmapInfoHeader->biSizeImage != 0)

        return m_pBitmapInfoHeader->biSizeImage;

else

    {

        DWORD height = (DWORD) GetHeight();

        DWORD width = (DWORD) GetWidth();

        return height * width;

    }

}

UINT CDib::GetWidth()

{

    return (UINT) m_pBitmapInfoHeader->biWidth;

}

UINT CDib::GetHeight()

{

    return (UINT) m_pBitmapInfoHeader->biHeight;

}

UINT CDib::GetNumberOfColors()

{

    int numberOfColors;

    if ((m_pBitmapInfoHeader->biClrUsed == 0) &&

          (m_pBitmapInfoHeader->biBitCount < 9))

//biClrUsed表示实际用到的颜色数,若0为2的biBitCount次中颜色

//biBitCount为用到的颜色的位数,小于9则表示最大为8位,那么之多为256色

{

   switch (m_pBitmapInfoHeader->biBitCount)

   {

      case 1: numberOfColors = 2; break;

      case 4: numberOfColors = 16; break;

      case 8: numberOfColors = 256;

   }

}

    else

   numberOfColors = (int) m_pBitmapInfoHeader->biClrUsed;//若不是上面的情况,则直接返回颜色数

    return numberOfColors;

}

注意,由于调用了函数GlobalFreePtr以及GlobalAllocPtr,则需要在头文件上添加相关的头文件:

#include "windowsx.h"

下面来分析一下这段源代码。其核心是LoadFile(),文件的读取,看一下该类成员函数的定义

UINT GetNumberOfColors();//返回位图颜色数目

UINT GetHeight();//返回位图高度

UINT GetWidth();//返回位图宽度

DWORD GetSize();//返回位图文件大小

void LoadFile(const char* dibFileName);//文件的读取操作

在LoadFile()函数中调用其他函数。

4.显示图像

显示图像当然要在View类中,因此图像的显示操作要在该类的OnDraw函数中进行

在该类中,我们要在菜单中点解“图像显示”按钮,然后才显示图像,所以我们要先响应菜单中“图像显示”命令。因此,右键“图像显示”,建立类向导,在 CTestView类中进行COMMAND响应,生成OnYuanTu函数,代码如下:

void CTestView::OnYuantu()

{

// TODO: Add your command handler code here

CTestDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

    filename=pDoc->filename;//注意区分一下,虽然都是叫做filename但是不同,右边这个filename是CTestDoc类的成员变量filename,存有文件的完整路径,左边这个是View类的filename

   state1=1;

Invalidate();//窗口重绘。当你的窗口需要重画时,你需要用Invalidate()来使窗口无效,然后会调用OnDraw()就会重画该窗口

}

当然,要在该类添加两个变量state1和filename,类型为int和CString.分别用来做按下“图像显示”按钮的标示,和存储文件名。

接下来,在点击“图像显示”按钮之后,就要在OnDraw函数中显示图像了,代码如下:

void CTestView::OnDraw(CDC* pDC)

{

//CTestDoc* pDoc = GetDocument();

//ASSERT_VALID(pDoc);

// TODO: add draw code for native data here

if(state1==1)

{

   CBitmap m_bitmap;

     HBITMAP hBitmap=(HBITMAP)LoadImage(NULL,_T(filename),IMAGE_BITMAP,

0,0,LR_CREATEDIBSECTION|LR_DEFAULTSIZE|LR_LOADFROMFILE);

//LoadImage函数载入图像,成功返回一个图像句柄,第一个参数是实例的句柄,这里不需要,_T(filename)文件名,第三个参数指读取的是位图(还可以有光标,图标),第四,第五个参数为0加上最后一个参数的|LR_DEFAULTSIZE,那么图像的大小将由资源本身决定,LR_CREATEDIBSECTION:当第三个参数指定为IMAGE_BITMAP时,使得函数返回一个DIB部分位图,而不是一个兼容的位图。这个标志在装载一个位图,而不是映射它的颜色到显示设备时非常有用。

        m_bitmap.Attach (hBitmap);

// Attach基本就是把一个句柄附加到一个mfc的对象上,比如你通过loadimage创建了一个handle,现在想用cbitmap类的成员函数,你就可以声明一个CBitmap对象,通过Attach将他们关联在一起,以后就可以使用CBitmap的成员函数来操作hBitmap了

     CDC dcImage;

     if(!dcImage.CreateCompatibleDC (pDC))

      return;

     BITMAP bm;

     m_bitmap.GetBitmap (&bm);//得到CBitmap对象的信息(BITMAP结构体),并存入那个参数(bm)中

        dcImage.SelectObject (&m_bitmap);

     pDC->BitBlt (0,0,bm.bmWidth ,bm.bmHeight ,&dcImage,0,0,SRCCOPY);

}

}

5.运行程序

好了,运行程序吧,打开一幅位图看看吧。呵呵

详解用VC实现bmp位图的打开 - fengqing888 - Forever Ralf_Volcano

说明:

(1). GlobalFreePtr(m_pBitmapInfo);

GlobalFreePtr函数从定义上看:

#define     GlobalFreePtr(lp)                \

            (GlobalUnlockPtr(lp), (BOOL)GlobalFree(GlobalPtrHandle(lp)))

定义为GlobalUnlockPtr与GlobalFree函数,

首先,其中GlobalFree函数为API函数,作用是释放指定内存空间并使句柄无效。其参数为一个句柄,因此看GlobalPtrHandle(lp),这个函数被定义为

#define     GlobalPtrHandle(lp)         \

                ((HGLOBAL)GlobalHandle(lp))

那么看GlobalHandle函数,作用是得到指针所指向内存的相关的句柄,函数前的(HGLOBAL)意思也是强制转换为句柄类型,注意看HGLOBAL的定义:

typedef HANDLE              HGLOBAL;

就是一个HANDLE而已。

接着看GlobalUnlockPtr函数,定义为

#define     GlobalUnlockPtr(lp)      \

                GlobalUnlock(GlobalPtrHandle(lp))

GlobalUnlock作用是解除被锁定的全局内存对象,参数为一个句柄,用之前GlobalPtrHandle得到句柄。

综上所述,可以看到GlobalFreePtr函数的作用就是解锁内存对象,释放内存并且使指针无效,通过多次定义,把几个API函数整合到一个新的函数中,简化操作而已。在这里,用途就是把m_pBitmapInfo指向的图像的内存与句柄释放并无效,意思就是清空m_pBitmapInfo的内容

(2)GlobalAllocPtr(GMEM_MOVEABLE, size)

通过查看定义发现,与上面的函数类似

#define     GlobalAllocPtr(flags, cb)        \

                (GlobalLock(GlobalAlloc((flags), (cb))))

GlobalLock函数锁定一个全局的内存对象,返回指向该对象的第一个字节的指针,其参数为一个句柄,那么接着由GlobalAlloc来获得句柄,GlobalAlloc函数是API函数,作用是分配一个全局内存块,返回一个句柄,起参数flags,如本例为GMEM_MOVEABLE 则为分配一个可移动内存块。第二个参数是分配的字符数size

综上所述,该函数的功能就是先分配内存,接着锁定内存块,然后返回指针,可以用该指针来操作该内存

小结:这几天的学习,总算明白了图像打开以及显示的大体思路以及操作方法,但在某些方面还是不太清楚,比如显示方面,有关设备上下文的操作不是很清楚,还有加强学习,呵呵。整体的框架思路来源于《VC++图像处理程序设计》这本书,特别感谢。希望大家指正。

参考资料:

《数字图像处理编程入门》 清华大学出版社 吕凤军编著

《VC++图像处理程序设计》 清华大学出版社 杨淑莹编著

《VC++深入详解》 电子工业出版社 孙鑫编著

《新编Windows API参考大全》 电子工业出版社

《数字图像处理基础》 科学出版社 朱虹编著

  评论这张
 
阅读(4186)| 评论(10)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018