图像的读入和显.ppt
20:37:53,冯筠:VC+图像编程,图像的读入和显示,西北大学信息学院冯筠 2009年11月,20:37:53,冯筠:VC+图像编程,ViewDIB 打开文件的例子,学习分隔视图创建方法(多文档多视图结构)学习串行化读取文件的机制图像读取和显示的基本流程在了解基本概念的基础上(API),了解图像类的设计方法学习的MFC类:CFileCArchive学习一些内存读取的API函数,20:37:53,冯筠:VC+图像编程,分隔窗的做法,在子窗口框架中定义分隔窗口对象 CSplitterWndm_wndSplitter;创建子窗口客户区时,定义分隔窗BOOL CChildFrame:OnCreateClient(LPCREATESTRUCT lpcs,CCreateContext*pContext)/TODO:Add your specialized code here and/or call the base classm_wndSplitter.CreateStatic(this,1,2);m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CViewDIBView),CSize(800,200),pContext);m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CResultView),CSize(800,200),pContext);m_wndSplitter.SetRowInfo(0,800,1);m_wndSplitter.SetColumnInfo(0,400,1);m_wndSplitter.SetColumnInfo(1,400,1);return TRUE;/return CMDIChildWnd:OnCreateClient(lpcs,pContext);,单文档,多视图结构,分辨率可以使用GetSystemMetrics(SM_CXSCREEN),20:37:53,冯筠:VC+图像编程,打开特定类型的文件,20:37:53,冯筠:VC+图像编程,串行化的概念,MFC的连续存储(serialize)机制俗称串行化在你的程序中尽管有着各种各样的数据,serialize机制会象流水一样按顺序存储到单一的文件中,而又能按顺序地取出,变成各种不同的对象数据。MFC 使用CArchive类管理不同的数据的存取,20:37:53,冯筠:VC+图像编程,CArchive 类概要,CArchive使用了缓冲区,即一段内存空间作为临时数据存储地,对CArchive的读写都先依次排列到此缓冲区,当缓冲区满或用户要求时,将此段整理后的数据读写到指定的存储媒质。当建立CArchive对象时,应指定其模式是用于缓冲区读,还是用于缓冲区写。可以这样理解,CArchive对象相当于铁路的货运练调度站,零散的货物被收集,当总量到达火车运量的时候,由火车装运走。当接到火车的货物时,则货物由被分散到各自的货主。与货运不同的是,交货、取货是按时间循序执行的,而不是凭票据。因此必须保证送货的和取货的货主按同样的循序去存或取。对于大型的货物,则是拆散成火车单位,运走,取货时,依次取各部分,组装成原物。,20:37:53,冯筠:VC+图像编程,CArchive 类概要,缓冲区指针 BYTE*m_lpBufStart,指向缓冲区,这个缓冲区有可能是底层CFile(如派生类CMemFile)对象提供的,但一般是CArchive自己建立的。缓冲区尾部指针 BYTE*m_lpBufMax;缓冲区当前位置指针 BYTE*m_lpBufCur;初始化时,如果是读模式,当前位置在尾部,如果是写模式,当前位置在头部:对于基本的数据类型,例如字节、双字等,可以直接使用“”、“”符号进行读出、写入。用GetFile()等函数可获得文件名等相关属性,用CArchive类进行读写的例子,20:37:53,冯筠:VC+图像编程,应用串行化打开文件的基本流程,对于一个文件而言,如果文件内数据的排列顺序是固定的,那么对于文件读和写从形式上只有使用的运算符的不同。在MFC的框架/文档/视图结构中,一个文档的内部对象的构成往往是固定的,这种情况下,写到文件中时对象在文件中的布局也是固定的。CDocument利用其基类CObject提供的Serilize虚函数,实现自动文档的读写。当用户在界面上选择文件菜单/打开文件(ID_FILE_OPEN)时,CWinApp派生类的OnFileOpen函数被自动调用,它通过文档模板创建(MDI)/重用(SDI)框架、文档和视图对象,并最终调用CDocument:OnOpenDocument来读文件,CDocument:OnOpenDocument 自动调用Serialize 函数,20:37:53,冯筠:VC+图像编程,自定义串行化对象类,从CObject派生该类实现了Serialize函数该类在定义时使用了DECLARE_SERIAL宏在类的实现文件中使用了IMPLEMENT_SERIAL宏这个类有一个不带参数的构造函数,或者某一个带参数的构造函数所有的参数都提供了缺省参数,20:37:53,冯筠:VC+图像编程,CDib 类-图像类,20:37:53,冯筠:VC+图像编程,CDib的串行化函数,20:37:53,冯筠:VC+图像编程,CDib 真正的读取函数,20:37:53,冯筠:VC+图像编程,图像类的基本结构,先定义一系列图像处理API函数C程序,包括读取,显示等等函数DibAPI.h DibAPI.cpp在API之上,设计CDib类定义了图像数据相关的一些属性将图像处理和打开的函数封装到类中基本分类情况打开图像(图像格式(讨论),读取方式)显示图像,20:37:53,冯筠:VC+图像编程,图像文件的格式,主要介绍BMP图像文件格式,并且文件里的图像数据是未压缩的(2色,4色,16色,256色,24位真彩色)因为图像的数字化处理主要是对图像中的各个像素进行相应的处理,而未压缩的BMP图像中的像素数值正好与实际要处理的数字图像相对应,这种格式的文件最合适我们对之进行数字化处理。压缩过的图像是无法直接进行数字化处理的,如JPEG、GIF等格式的文件,此时首先要对图像文件解压缩,这就要涉及到一些比较复杂的压缩算法,本课程不涉及。,20:37:53,冯筠:VC+图像编程,BMP文件格式,文件头(BITMAPFILEHEADER)主要包含文件的大小、文件类型、图像数据偏离文件头的长度等信息位图信息头(BITMAPINFOHEADER)包含图象的尺寸信息、图像用几个比特数值来表示一个像素、图像是否压缩、图像所用的颜色数等信息颜色信息(RGBQUAD)颜色信息包含图像所用到的颜色表,显示图像时需用到这个颜色表来生成调色板,但如果图像为真彩色,即图像的每个像素用24个比特来表示,文件中就没有这一块信息,也就不需要操作调色板图形数据表示图像的相应的像素值,图像的像素值在文件中的存放顺序为从左到右,从下到上,文件存储图像的每一行像素值时,如果存储该行像素值所占的字节数为4的倍数,则正常存储,否则,需要在后端补0,凑足4的倍数。,20:37:53,冯筠:VC+图像编程,图像格式:BMP文件头,BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。其结构定义如下:typedef struct tagBITMAPFILEHEADERWORD bfType;/位图文件的类型,必须为“BM”DWORD bfSize;/位图文件的大小,以字节为单位WORD bfReserved1;/位图文件保留字,必须为0WORD bfReserved2;/位图文件保留字,必须为0DWORD bfOffBits;/位图数据的起始位置,以相对于位图文件头的偏移量表示,以字节为单位 BITMAPFILEHEADER;该结构占据14个字节。,20:37:53,冯筠:VC+图像编程,图像格式:位图信息头,BMP位图信息头数据用于说明位图的尺寸等信息。其结构如下:typedef struct tagBITMAPINFOHEADERDWORD biSize;/本文件所占用字节数LONG biWidth;/位图的宽度,以像素为单位LONG biHeight;/位图的高度,以像素为单位WORD biPlanes;/目标设备的平面数,一般为1WORD biBitCount/每个像素所需的位数,必须是1(双色),4(16色),8(256色)或24(真彩色)之一DWORD biCompression;/位图压缩类型,必须是 0(不压缩),1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一DWORD biSizeImage;/位图的大小,以字节为单位LONG biXPelsPerMeter;/位图水平分辨率,每米像素数LONG biYPelsPerMeter;/位图垂直分辨率,每米像素数DWORD biClrUsed;/位图实际使用的颜色表中的颜色数DWORD biClrImportant;/位图显示过程中重要的颜色数 BITMAPINFOHEADER;该结构占据40个字节。,20:37:53,冯筠:VC+图像编程,图像格式:颜色表,颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:typedef struct tagRGBQUAD BYTE rgbBlue;/蓝色的亮度(值范围为0-255)BYTE rgbGreen;/绿色的亮度(值范围为0-255)BYTE rgbRed;/红色的亮度(值范围为0-255)BYTE rgbReserved;/保留,必须为0 RGBQUAD;,20:37:53,冯筠:VC+图像编程,图像格式:位图信息,颜色表中RGBQUAD结构数据的个数由BITMAPINFOHEADER 中的biBitCount项来确定,当biBitCount=1,4,8时,分别有2,16,256个颜色表项,当biBitCount=24时,图像为真彩色,图像中每个像素的颜色用三个字节表示,分别对应R、G、B值,图像文件没有颜色表项。位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:typedef struct tagBITMAPINFO BITMAPINFOHEADER bmiHeader;/位图信息头RGBQUAD bmiColors1;/颜色表头指针 BITMAPINFO;RGBQUAD结构中定义的颜色值中,红色、绿色和蓝色的排列顺序与一般真彩色图像文件的颜色数据排列顺序恰好相反,20:37:53,冯筠:VC+图像编程,图像格式:位图数据,位图数据记录了位图的每一个像素值或该对应像素的颜色表的索引值,图像记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:当biBitCount=1时,8个像素占1个字节;当biBitCount=4时,2个像素占1个字节;当biBitCount=8时,1个像素占1个字节;当biBitCount=24时,1个像素占3个字节,此时图像为真彩色图像。当图像不是为真彩色时,图像文件中包含颜色表,位图的数据表示对应像素点在颜色表中相应的索引值,当为真彩色时,每一个像素用三个字节表示图像相应像素点彩色值,每个字节分别对应R、G、B分量的值,这时候图像文件中没有颜色表。,20:37:53,冯筠:VC+图像编程,图像格式:位图数据的计算,Windows规定图像文件中一个扫描行所占的字节数必须是4的倍数(即以字为单位),不足的以0填充,图像文件中一个扫描行所占的字节数计算方法:DataSizePerLine=(biWidth*biBitCount+31)/8;/一个扫描行所占的字节数位图数据的大小按下式计算(不压缩情况下):DataSize=DataSizePerLine*biHeight。,20:37:53,冯筠:VC+图像编程,打开图像做的主要工作,将流数据读入相应的数据结构 BITMAPFILEHEADER,BITMAPINFOHEADER BITMAPINFO RGBQUAD创建位图数据块(m_pDIB指向的数据块)创建调色板(m_pPalette指向的数据块)创建CDC需要的位图(m_Bitmap),20:37:53,冯筠:VC+图像编程,存储图像的数据结构,在文档类中:CDib*m_pDib;/原始图像对象指针(右)CDib*m_pResultDib;/显示或处理效果图像对象指针在CDib类中CBitmap*m_pBitmap;/位图指针CPalette*m_pPalette;/调色板指针HBITMAP m_hBitmap;/位图块句柄HDIB m_hDib;/设备无关位图句柄,20:37:53,冯筠:VC+图像编程,读取时用到的函数,hDIB=GlobalAlloc(GHND,dwSize)分配内存,返回句柄lpBI=(LPBYTE)GlobalLock(hDIB)将该内存块全局锁定,返回指针GlobalUnlock(hDib);解锁强制类型转换lpbi=(LPBITMAPINFOHEADER)lpBICopyMemory(lpbi,lpDIB,dwSize)内存复制,20:37:53,冯筠:VC+图像编程,读取中遇到的CFile函数,pFile-Read(将文件指针恢复到文件头,20:37:53,冯筠:VC+图像编程,跟踪打开(读入)图像文件的过程,ON_COMMAND(ID_FILE_OPEN,CWinApp:OnFileOpen)CViewDIBDoc:OnOpenDocument-CViewDIBDoc:Serialize-CDib:Serialize-CDib:Read(CFile*pFile)(*重点看,已注释)CDib:Create(LPBYTE lpDIB)-(*重点看,已注释)CDib:UpdateInternal-(*创建调色板,创建位图)CDib:BuildPalette()-(*已标注下节课详讲)DIBAPI-CreateDIBPalette-CDib:BuildBitmap()(*已标注下节课详讲)DIBAPI-DIBToDIBSection,20:37:53,冯筠:VC+图像编程,显示图像做的主要工作,将创建好的位图联系到设备上下文中(pDC-SelectObject)在OnDraw函数中绘制图像,20:37:53,冯筠:VC+图像编程,图形设备接口(GDI),GDI是Windows提供的一套函数和结构,以便于我们调用它们来绘图。因为我们有不同的输出设备,各种显示器,各种打印机,他们有不同的打印驱动程序,GDI提供这样一个平台,屏蔽了他们的差异。使用的GDI全部使用设备上下文(DC)作为显示设备的信息来源。因此,我们无需关心设备的特性。在图形绘制当中,提供了一个叫做设备上下文(DC)的结构,是一个GDI提供的接口供我们来访问设备,所有的绘图都是通过设备上下文来进行。因此,同一应用程序可以在配有不同的类型显示器的计算机上使用。应用程序不需要针对所有显示器进行更改,20:37:53,冯筠:VC+图像编程,设备描述表类(CDC),当 Windows 程序在屏幕、打印机或其他输出设备上画图时,它并不是将像素直接输出到设备上,而是将图绘制到由设备描述表(DC)表示的逻辑意义上的“显示平面”上去.设备描述表是一种数据结构,它包含 GDI 需要的所有关于显示平面情况的描述字段,包括相连的物理设备和各种各样的状态信息。MFC 的 CDC 类将 Windows 设备描述表和获取设备描述表句柄的 GDI 函数就近封装在一起,而 CDC 派生类如 CPaintDC 和 CClientDC 则代表 Windows 应用程序使用的不同类型的设备描述表。,20:37:53,冯筠:VC+图像编程,CDC的用法,在 MFC 应用程序中获取设备描述表的一种方法是调用CWnd:GetDC,它返回指向表示 Windows 设备描述表的 CDC 对象的指针。在画图完毕时,要用 CWnd:ReleaseDC 释放由 CWnd:GetDC 获取的设备描述表指针 CPaintDC用于在窗口客户区画图(仅限于 OnPaint 处理程序)CClientDC用于在窗口客户区画图(除 OnPaint 外的任何处理程序)CWindowDC用于在窗口内任意地方画图,包括非客户区CMetaFileDC用于向 GDI 元文件画图,20:37:53,冯筠:VC+图像编程,在CDC上绘画,CDC进行绘画的时候,可以使用多种资源,例如:pDC-SelectObject(CPen*pPen);pDC-SelectObject(CBrush*pBrush);pDC-SelectObject(CFont*pFont);pDC-SelectObject(CBitmap*pBitmap);pDC-SelectObject(CRgn*pRgn);pDC-SelectPalette(m_pPalette,TRUE),20:37:53,冯筠:VC+图像编程,图像的显示,显示DIB位图数据可以通过设备上下文CDC对象的成员函数CDC:Bitblt()或CDC:StretchBlt()来实现,也可以通过API函数SetDIBBitsToDevice()或StretchDIBBits()来实现StretchDIBBits()和CDC:StretchBlt()可以将图像进行放大和缩小显示。当从文档中装入位图文件时,CDIBView类的OnInitialUpdate函数将被调用,因此可以在该函数中实现对视图尺寸的设置,用于正确的显示位图,然后就可以在视图类的OnDraw()函数中正确的显示位图了。,20:37:53,冯筠:VC+图像编程,CDC中画图像的方法,CViewDIBView:OnDraw()pDoc-m_pDib-Display(pDC,0,0)一般采用双缓冲技术,防止闪屏 CDC MemDC;MemDC.CreateCompatibleDC(pDC);/创建一个内存的中上下文草稿/选择位图资源CBitmap*pOldBmp=MemDC.SelectObject(m_pBitmap);/进行位拷贝 BOOL bSuccess=pDC-BitBlt(x,y,GetWidth(),GetHeight(),/恢复过去的上下文环境,20:37:53,冯筠:VC+图像编程,参考书,“精通VC+图像编程”:周长发网上视频,