第2章图像的显示.docx
- 文档编号:11315623
- 上传时间:2023-02-26
- 格式:DOCX
- 页数:26
- 大小:237.15KB
第2章图像的显示.docx
《第2章图像的显示.docx》由会员分享,可在线阅读,更多相关《第2章图像的显示.docx(26页珍藏版)》请在冰豆网上搜索。
第2章图像的显示
第2章图像的显示
本章要点:
☑调色板的基本应用
☑图像的特效显示
2.1调色板的基本应用
图像显示是最基本同时也是最重要的一种图像处理技术。
因为对于计算机来说,图像显示几乎无处不用,要学好图像处理,首先就要了解显示的原理和实现的方法。
现实世界的颜色很多,但计算机只能表示其中的部分颜色。
每一种颜色经过数字化处理之后,转变成了数字的形态,即由若干个位(Bit)组成,图像中能够表示的颜色与每一种颜色所需的位数有关。
每一种颜色所需位数与存储颜色的情况如下:
1位2种颜色
2位4种颜色
4位16种颜色
8位256种颜色
16位65536种颜色
24位1677万种颜色
32位1677万种颜色和256级灰度值
36位687亿种颜色和4096级灰度值
为了计算机能更好的重现实际图像,就需要采用一定的技术来管理颜色。
微软WindowsNT,Windows95/98等提供了真彩色和调色板两种相似的处理颜色的方法。
通常所称的标准VGA显示模式是8位显示模式,即在该模式下能显示256种颜色;而高彩色(HICOLOR)显示是16位显示模式,能显示65536种颜色,也称64K色;还有一种真彩色(TRUECOLOR)显示模式是24位显示模式,能显示1677万种颜色,也称16M色,这是现在一般PC机所能达到的最高颜色显示模式,在该模式下看到的真彩色图像的色彩已和高清晰度照片没什么差别了。
在Windows操作系统中不是真彩色系统的位图文件中都含有颜色索引表,在显示时可以采用调色板技术,将文件中自带的颜色显示出来。
在第一章已经介绍了256色位图文件中含有BMP文件由文件头、位图信息头、颜色信息表和图像数据四部分组成。
256色位图文件输入到计算机后,在计算机内通常采用二维数组来表示图像的矩阵。
位图文件中所有颜色信息表组成颜色索引表,颜色索引表中装有该文件所有颜色的R、G、B各分量值,每个像素的像素值是颜色索引表的索引号。
调色板中的颜色值指定了屏幕上像素的红、绿、蓝三个基色的混合比例,调色板就是在256色显示系统中,将图像中出现的256种颜色组成颜色表,对这些颜色按8位,即0至255进行编号,每一编号代表其中的一种颜色,在这种颜色中颜色编号叫做颜色的索引号。
屏幕上的每个像素对应一个颜色号,不同的像素的颜色对应不同的调色板颜色值。
图像的像素值并不是颜色值,而是颜色在调色板查找表中的索引号。
调色板是一组独立于存储各个像素颜色编号存储区的视频存储区。
为了确定每个颜色编号所对应的真实颜色,显示硬件要参考调色板的颜色值。
在调色板系统中,每一幅图像都有自己的调色板,显示时必须将自己的调色板载入系统调色板中,实现调色板。
1.调色板的原理
Windows的调色板管理十分复杂,每一个Windows应用程序都由自己的调色板,在使用调色板时首先要向Windows提出申请,Windows根据该应用程序的优先级,来对程序调色板进行分配,一个优先级最高的应用程序可以得到Windows的前台调色板,其他的窗口使用后台调色板。
为了保证Windows基本显示界面的一致性,Windows保留了一个有20种颜色的内部系统调色板,用来描绘窗口的图标、边界、按钮等通用界面。
该调色板在所有的显示设置中都保持不变。
每个设备上下文都拥有一个逻辑调色板,如果要使用内部系统调色板之外的颜色,应该创建一个新的逻辑调色板并将其选入设备上下文中。
但仅仅这样还不能使用新的颜色,程序只有把设备上下文中的逻辑调色板实现到系统调色板中,新的颜色才能实现。
在逻辑调色板被实现到系统调色板时,Windows会建立一调色板映射表。
当设备上下文用逻辑调色板中的颜色绘图时,GDI绘图函数会查询调色板映射表以把像素值从逻辑调色板的索引转化为系统调色板的索引,这样像素被输出到视频内存中就具有了正确的颜色值。
2.调色板的创建与实现
在Windows中使用了专门的调色板管理器对调色板进行管理,对逻辑调色板和系统调色板之间的映射进行操作。
在使用VC++进行Windows的应用程序设计时,MFC基本类库提供了CDC类和CPlatte类,封装了有关调色板的操作,使调色板的操作得以简化。
该类的成员函数CreatePalette负责创建逻辑调色板。
一般创建一个逻辑调色板需要以下五步:
(1)建立一个LOGPALETTE结构和PALETTEENTRY数组;
(2)对数组元素进行初始化并对成员变量进行设置;
(3)建立CPalette对象并使用CreatePalette函数初始化调色板对象;
(4)使用SelectPalette函数来将设备描述表和调色板联系起来;
(5)使用CDC中的RealizePalette函数使调色板生效。
3.创建调色板程序代码
(1)在视类定义一个指向调色板的指针。
CPalette*hPalette;
(2)为要显示的位图创建调色板,要显示的位图可以是CDib或CDib派生类的对象或指针。
CPalette*CDynSplitView2:
:
CreateBitmapPalette(CDib*pBitmap)
{
struct
{
WORDVersion;
WORDNumberOfEntries;
PALETTEENTRYaEntries[256];
}palette={0x300,256};
LPRGBQUADpRGBTable=pBitmap->GetRGB();
UINTnumberOfColors=pBitmap->GetNumberOfColors();
for(UINTx=0;x { palette.aEntries[x].peRed=pRGBTable[x].rgbRed; palette.aEntries[x].peGreen=pRGBTable[x].rgbGreen; palette.aEntries[x].peBlue=pRGBTable[x].rgbBlue; palette.aEntries[x].peFlags=0; } //hPalette已在视类定义 hPalette.CreatePalette((LPLOGPALETTE)&palette); return&hPalette; } 4.图像的显示 当取到了图像数据首地址,就可以用图像文件自带的颜色表,创建调色板,载入调色板显示图像。 程序为: voidCDynSplitView2: : OnDraw(CDC*pDC) { intm_scale=1;//控制缩放比例 BYTE*pBitmapData=CDib->GetData();//CDib类的指针对象,其内容参见下一节 LPBITMAPINFOpBitmapInfo=CDib->GetInfo(); intbitmapHeight=CDib->GetHeight(); intbitmapWidth=CDib->GetWidth(); intscaledWidth=(int)(bitmapWidth*m_scale); intscaledHeight=(int)(bitmapHeight*m_scale); if(CDib->GetRGB())//Hasacolortable { CPalette*hPalette=CreateBitmapPalette(CDib); //将已创建的调色板调用到设备上下文中 CPalette*hOldPalette=pDC->SelectPalette(hPalette,true); //实现调色板 pDC->RealizePalette(); : : StretchDIBits(pDC->GetSafeHdc(),0,0,scaledWidth,scaledHeight, 0,0,bitmapWidth,bitmapHeight,pBitmapData,pBitmapInfo, DIB_RGB_COLORS,SRCCOPY); pDC->SelectPalette(hOldPalette,true); : : DeleteObject(hPalette); } } 本书中所有实例都对经过算法处理后的图像在显示方面创建调色板,载入调色板,来显示处理后的图像。 图2-1显示结果 5.显示函数 在VisualC++环境中用于显示的主要有BitBlt函数、StretchBlt函数和StretchDIBits函数。 BitBlt函数和StretchBlt函数属于CDC类,而StretchDIBits函数为API函数。 下面介绍这三个函数: (1)BitBlt()函数 该函数的结构如下: BOOLBitBlt(intx,inty,intnWidth,intnHeight,CDC*pSrcDC,intxSrc,intySrc,DWORDdwrop); 参数说明: x: 指定绘制目标矩形左上角的逻辑X轴位置。 y: 指定绘制目标矩形左上角的逻辑Y轴位置。 nWidth: 指定绘制目标矩形和源位图的宽度(按逻辑单位)。 nHeight: 指定绘制目标矩形和源位图的高度(按逻辑单位)。 pSrcDC: 设备上下文的指针。 xSrc,ySrc: 指定源位图左上角的逻辑X轴、Y轴位置。 dwrop: 指定要执行的光栅运算。 其操作码定义了图形设备接口(GDI)在输出操作中如何组合颜色。 当要将位图的像素从内存显示环境复制到显示器(或打印机)设备环境中,一般会用到这个函数。 (2)StretchBlt()函数 该函数的结构如下: BOOLStretchBlt(intx,inty,intnWidth,intnHeight,CDC*pSrcDC,intxSrc,intySrc,intnSrcWidth,intnSrcHeight,DWORDdwrop); 参数说明: x: 指定绘制目标矩形左上角的逻辑X轴位置。 y: 指定绘制目标矩形左上角的逻辑Y轴位置。 nWidth: 指定绘制目标矩形的宽度(按逻辑单位)。 nHeight: 指定绘制目标矩形的高度(按逻辑单位)。 pSrcDC: 设备上下文的指针 xSrc,ySrc: 指定源位图左上角的坐标(按逻辑单位)。 nSrcWidth,nSrcHeight: 指定复制源位图的宽度和高度(按逻辑单位)。 dwrop: 指定要执行的光栅运算。 其操作码定义了图形设备接口(GDI)在输出操作中如何组合颜色。 此函数可以对图像进行缩放处理。 (3)StretchDIBits()函数 该函数的结构如下: BOOLStretchDIBits(HDChdc,intx,inty,intnWidth,intnHeight,intxSrc,intySrc,intnSrcWidth,intnSrcHeight,CONSTVOID*lpvBits,CONSTBITMAPINFO*lpbmi,UNITfuColorUse,DWORDdwrop); 参数说明: hdc: 设备上下文句柄。 x: 指定绘制目标矩形左上角的逻辑X轴位置。 y: 指定绘制目标矩形左上角的逻辑Y轴位置。 nWidth: 指定绘制目标矩形的宽度(按逻辑单位)。 nHeight: 指定绘制目标矩形的高度(按逻辑单位)。 xSrc,ySrc: 指定源位图左上角的坐标(按逻辑单位)。 nSrcWidth,nSrcHeight: 指定复制源位图的宽度和高度(按逻辑单位)。 lpvBits: 指向DIB数据图像的指针。 lpbmi: 指向BITMAPINFO结构的指针。 fuColorUse: 指定BITMAPINFO结构中的bmiColors包含真实的RGB值还是调色板中的索引值。 dwrop: 指定要执行的光栅运算。 其操作码定义了图形设备接口(GDI)在输出操作中如何组合颜色。 2.2图像的特效显示 图像的特效显示就是利用人眼的视觉特性,通过对图像分块,然后以不同的次序显示出来。 实现图像的特效显示的基本思路是将图像分成不同的小块,按一定的方向或次序、分阶段地显示或擦除图像块。 其中的四个要点是: (1)如何划分图像块; (2)确定图像块的操作次序; (3)显示或清除图像块; (4)在两个图像块的操作之间延时。 延时的目的是减慢图像的显示速度,以便可以看出特效显示的效果。 本章将按效果实现的难易,来介绍几个常见的特效显示。 ø图像的扫描; ø图像的移动; ø交叉飞入; ø中间扩张; ø中间收缩; ø栅条特效; ø图像渐显; ø百叶窗特效显示; ø马赛克效果。 2.2.1图像的扫描 扫描是最基本的特效显示方式,它没有划分图像块,只是顺序地一行一行或一列一列地显示图像或清除图像。 下面的两段程序分别是向下扫描和向上扫描的原代码。 1.向下扫描 编程代码: /*向下扫描特效显示*/ voidCDynSplitView2: : OnXiangxia() { //刷新屏幕 CDC*pDC=GetDC(); CRectrect(0,0,1000,1000); CBrushbrush(RGB(255,255,255)); pDC->FillRect(&rect,&brush); //复制图像数据 clearmem(); CDSplitDoc*pDoc=GetDocument(); ASSERT_VALID(pDoc); if(! pDoc->statedoc&&state2==1) { BYTE*pBitmapData=CDibNew1->GetData(); LPBITMAPINFOpBitmapInfo=CDibNew1->GetInfo(); intbitmapHeight=CDibNew1->GetHeight(); intbitmapWidth=CDibNew1->GetWidth(); if(CDibNew1->GetRGB())//Hasacolortable { CPalette*hPalette=CreateBitmapPalette(CDibNew1); CPalette*hOldPalette=pDC->SelectPalette(hPalette,true); pDC->RealizePalette(); for(intj=0;j { : : StretchDIBits(pDC->GetSafeHdc(),0,j,bitmapWidth,1, 0,bitmapHeight-j,bitmapWidth,1, pBitmapData,pBitmapInfo, DIB_RGB_COLORS,SRCCOPY); Sleep(5); } pDC->SelectPalette(hOldPalette,true); : : DeleteObject(hPalette); } } Invalidate(); } 向上扫描、向右扫描和向左扫描的原理和向下扫描相同,只是显示图像的次序不同,这里我们只给出不同部分的程序代码。 2.向上扫描 for(intj=0;j { : : StretchDIBits(pDC->GetSafeHdc(),0,bitmapHeight-j,bitmapWidth,1, 0,j,bitmapWidth,1,pBitmapData,pBitmapInfo, DIB_RGB_COLORS,SRCCOPY); Sleep(5); } 3.向右扫描 for(inti=0;i { : : StretchDIBits(pDC->GetSafeHdc(),i,0,1,bitmapHeight, i,0,1,bitmapHeight,pBitmapData,pBitmapInfo, DIB_RGB_COLORS,SRCCOPY); Sleep(5); } 4.向左扫描 for(inti=0;i { : : StretchDIBits(pDC->GetSafeHdc(),bitmapWidth-i,0,1,bitmapHeight, bitmapWidth-i,0,1,bitmapHeight,pBitmapData,pBitmapInfo, DIB_RGB_COLORS,SRCCOPY); Sleep(5); } 5.效果图 (a)自上而下扫描(b)自下而上扫描 (c)自左向右扫描(d)自右向左扫描 图2-2图像的扫描 2.2.2图像的移动 移动是将图像看作一个整体,显示时不能像扫描那样,扫描方式有些像打开一幅画,例如显示上部分的时候,下部分可以不显示,而移动则可以看成一块木板画,显示时必须按物理顺序进行,例如从上向下平移时,必须先显示下面的图像,后显示上面的图像。 因此平移的算法比扫描要难一些,平移是以复制的方法显示图像的,每显示一次,复制的行数就增加一行,直至显示完成。 下面两段程序分别是水平左移和水平右移的关键代码。 1.水平右移 /*水平右移特效显示*/ for(inti=0;i<=bitmapWidth;i++) { for(intj=0;j<=bitmapHeight;j=j+8) { : : StretchDIBits(pDC->GetSafeHdc(),0,j-8,i+1,8, bitmapWidth-i,bitmapHeight-j,i+1,8,pBitmapData,pBitmapInfo, DIB_RGB_COLORS,SRCCOPY); } } 2.水平上移 水平上移的原理和水平右移相同,只是复制图像的次序不同,这里我们只给出不同部分的程序代码: for(inti=0;i<=bitmapWidth;i++) { for(intj=0;j<=bitmapHeight;j=j+8) { : : StretchDIBits(pDC->GetSafeHdc(),j,bitmapHeight-i,8,i, j,bitmapHeight-i,8,i,pBitmapData,pBitmapInfo, DIB_RGB_COLORS,SRCCOPY); } } 3.效果图 (a)水平向右移(b)垂直向上移动 图2-3图像的移动 2.2.3交叉飞入 交叉飞入是将图像平分成上下两部分,显示时上部分水平右移,下部分水平左移。 因此交叉分入的基本原理和平移是相同的,其不同之处只是将图像进行了分块。 下面是交叉飞入的程序关键原代码: for(inti=0;i<=bitmapWidth;i++) { for(intj=0;j<=bitmapHeight/2;j=j+nscanline) { : : StretchDIBits(pDC->GetSafeHdc(),0,j,i+1,nscanline, bitmapWidth-i,bitmapHeight-j-nscanline,i+1,nscanline, pBitmapData,pBitmapInfo, DIB_RGB_COLORS,SRCCOPY); k=j+nscanline+bitmapHeight/2; : : StretchDIBits(pDC->GetSafeHdc(),bitmapWidth-i,k,i+1,nscanline,0,bitmapHeight-k,i+1,nscanline, pBitmapData,pBitmapInfo, DIB_RGB_COLORS,SRCCOPY); } } 图2-4交叉飞入 2.2.4中间扩张 当我们打开电视机的时候,都有这样的感觉,电视图像是从屏幕中间开始,向上下两方向展开显示。 这种效果就是中间扩张,中间扩张特效显示的原理其实并不难,在显示的时候,先将图像分成两部分,将中间分界处显示在屏幕的中间,并快速向上扫描上半部分的图像,然后将图像完整的显示在屏幕上,这样人们因为视觉生理的特点就会看到中间扩张的效果。 下面是中间扩张的程序关键代码: for(inti=0;i<=bitmapWidth/2;i++) { : : StretchDIBits(pDC->GetSafeHdc(),bitmapWidth/2-i,bitmapHeight/2-i, i*2,i,bitmapWidth/2-i,bitmapHeight/2-i,i*2,i, pBitmapData,pBitmapInfo, DIB_RGB_COLORS,SRCCOPY); : : StretchDIBits(pDC->GetSafeHdc(),bitmapWidth/2-i,bitmapHeight/2-i, i*2,i*2,bitmapWidth/2-i,bitmapHeight/2-i,i*2,i*2, pBitmapData,pBitmapInfo, DIB_RGB_COLORS,SRCCOPY); Sleep(30); } 图2-5中间扩张 2.2.5中间收缩 中间收缩的效果是从屏幕的上下两边同时向中间扫描,其原理类似于将图像平分成上下两部分,从屏幕的上下边界,对图像同时进行向上扫描和向下扫描,只到在图像的中间分界相遇。 下面是中间收缩程序关键代码: /*中间收缩特效显示*/ for(intj=0;j { : : StretchDIBits(pDC->GetSafeHdc(),0,j-1,bitmapWidth,1, 0,bitmapHeight-j,bitmapWidth,1, pBitmapData,pBitmapInfo, DIB_RGB_COLORS,SRCCOPY); : : StretchDIBits(pDC->GetSafeHdc(),0,bitmapHeight-j,bitmapWidth,1, 0,j,bitmapWidth,1, pBitmapData,pBitmapInfo, DIB_RGB_COLORS,SRCCOPY); Sleep(5); } 图2-6中间收缩 2.2.6栅条特效 栅条特效分为水平栅条和垂直栅条,其效果像是将两手交叉的过程,栅条显示的原理是先将图像分
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 图像 显示