申请书范文网,分享全网优秀范文,学习好帮手!
申请书范文网 > 数字图像处理(极简) 第三章 BMP文件的读取与显示(docx)

数字图像处理(极简) 第三章 BMP文件的读取与显示(docx)

时间:2023-01-13 06:56:55

相关推荐

数字图像处理(极简) 第三章 BMP文件的读取与显示(docx)

建议先修课程:高等数学(微积分)、线性代数。

参考书目:

1、图像工程(上册)——图像处理(第4版) 章毓晋 清华大学出版社

链接:/s/1hEMGRUotQFL_RtGap6JaUg

提取码:0000

三 BMP文件的读取与显示

BMP文件是一个非常简单的图像存储格式。学习处理图像时,我们先从最简单的格式入手。处理其它复杂的格式时,往往采用先解压缩再进行后续处理的方法。解压缩后的数据往往与BMP等非压缩格式(通常是,压缩的BMP很少见)相仿。

BMP文件主要应用于Windows平台。因此,继续下面的学习之前,你应当具备一些Windows编程的知识。

BMP文件的结构分为四部分:

【1】文件头。

【2】信息头。

【3】调色板(可选)。

【4】图像数据。

文件头的格式定义在<wingdi.h>中:

typedef struct tagBITMAPFILEHEADER {

WORD bfType;

DWORD bfSize;

WORD bfReserved1;

WORD bfReserved2;

DWORD bfOffBits;

} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;

其中

<minwindef.h>:

#define far

#define FAR far

typedef unsigned long DWORD;

typedef unsigned short WORD;

这个结构的长度固定为14字节。各个成员的解释如下:

bfType

指定文件类型,其值为42 4D,即BM。也就是说所有.bmp文件的头两个字节都是“BM”。

bfSize

指定文件大小(包括文件头)。该值为DWORD值,即双字(4字节),因此BMP文件的大小最大为4 GB(232字节)。

bfReserved1,bfReserved2

保留字,这里不考虑。

bfOffBits

为从文件头到实际的位图数据的偏移字节数。

信息头的格式也定义在<wingdi.h>中:

typedef struct tagBITMAPINFOHEADER{

DWORD biSize;

LONG biWidth;

LONG biHeight;

WORD biPlanes;

WORD biBitCount;

DWORD biCompression;

DWORD biSizeImage;

LONG biXPelsPerMeter;

LONG biYPelsPerMeter;

DWORD biClrUsed;

DWORD biClrImportant;

} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;

其中

<winnt.h>:

typedef long LONG;

这个结构的长度固定为40字节。各个成员的解释如下:

biSize

值为常量40,指定此结构的长度。

biWidth,biHeight

指定图像的宽和高(单位:像素)。

biPlanes

值固定为1,这里不考虑。

biBitCount

指定表示颜色时要用到的位数,取值可以为:1(黑白二色图)、4(16色图)、8(256色)、24(真彩色图)。

biCompression

指定位图是否压缩。Windows位图可以采用压缩格式,但用得不多。我们只讨论不压缩的情况,biCompression为BI_RGB。

<wingdi.h>:

#define BI_RGB 0L

#define BI_RLE8 1L

#define BI_RLE4 2L

#define BI_BITFIELDS 3L

#define BI_JPEG 4L

#define BI_PNG 5L

biSizeImage

在图像为压缩格式时,刻画位图数据的长度。图像未压缩时,此值为零。

biXPelsPerMeter,biYPelsPerMeter

指定目标设备的水平和垂直分辨率。单位:每米的像素个数。

biClrUsed

指定本图像实际用到的颜色数(决定调色板数组元素的个数),如果该值为零,则用到的颜色数为2的biBitCount次方。

一个规范的BMP文件应当只有在真彩色时才将该值置为零。

biClrImportant

指定本图像中重要的颜色数量。该值通常为零,即认为所有的颜色都是重要的。

当图像不是真彩色时,在信息头之后还有调色板。调色板实际上是一个数组,共有biClrUsed个元素(如果该值为零,则有2的biBitCount次方个元素)。数组中每个元素的是一个RGBQUAD结构,占4个字节,其定义位于<wingdi.h>:

typedef struct tagRGBQUAD {

BYTE rgbBlue;

BYTE rgbGreen;

BYTE rgbRed;

BYTE rgbReserved;

} RGBQUAD;

一个标准的256灰度图像,其调色板的第0到255项的前三项rgbBlue、rgbGreen、rgbRed均分别为0到255。后续的位图数据中,每1个字节代表1个像素,索引值正好就是灰度值。

对于用到调色板的位图,图像数据就是该像素颜色在调色板中的索引值;对于真彩色图,图像数据就是实际的RGB值。下面就2色、16色、256色位图和真彩色位图分别介绍。

2色位图,用1位就可以表示该像素的颜色(一般0表示黑,1表示白),一个字节可以表示8个像素。

16色位图,用4位可以表示一个像素的颜色,所以一个字节可以表示2个像素。

256色位图,一个字节表示1个像素。

真彩色图,三个字节表示1个像素。

但是每一行的字节数并不简单等于宽度×单个像素占用的空间。BMP格式要求:每一行的字节数必须是4的整数倍。如果不是,则需要在一行的位图数据后补齐。

BMP文件的数据是从下到上,从左到右的。也就是说,从文件中最先读到的是图像最下面一行的左边第一个像素,然后是左边第二个像素……接下来是倒数第二行左边第一个像素,左边第二个像素……依次类推,最后得到的是最上面一行的最右一个像素。

在应用程序的GUI中显示位图,需要用到<wingdi.h>中的Windows API函数StretchDIBits:

StretchDIBits函数将DIB,JPEG或PNG图像中像素矩形的颜色数据复制到指定的目标矩形。如果目标矩形大于源矩形,则此函数会拉伸颜色数据的行和列以适合目标矩形。如果目标矩形小于源矩形,则此函数使用指定的栅格操作压缩行和列。

其语法如下:

int StretchDIBits(

HDC hdc,

int xDest,

int yDest,

int DestWidth,

int DestHeight,

int xSrc,

int ySrc,

int SrcWidth,

int SrcHeight,

const VOID * lpBits,

const BITMAPINFO* lpbmi,

UINT iUsage,

DWORD rop

);

其中

<winnt.h>:

#define VOID void

<minwindef.h>:

typedef unsigned int UINT;

每个参数的解释如下:

hdc

目标设备上下文的句柄。

设备上下文也称设备描述表,是一种Windows数据结构,其中包含有关设备(例如显示器或打印机)的绘图属性的信息。 所有绘图调用都通过设备上下文对象进行,该对象封装用于绘制线条、形状和文本的Windows API。设备上下文允许Windows中与设备无关的绘图。设备上下文可用于绘制到屏幕、打印机或元文件(metafile)。

xDest

目标矩形左上角的x坐标(以逻辑单位表示)。

yDest

目标矩形左上角的y坐标(以逻辑单位表示)。

DestWidth

目标矩形的宽度,以逻辑单位为单位。

DestHeight

目标矩形的高度,以逻辑单位为单位。

xSrc

图像中源矩形的x坐标(以像素为单位)。

ySrc

图像中源矩形的y坐标(以像素为单位)。

SrcWidth

图像中源矩形的宽度(以像素为单位)。

SrcHeight

图像中源矩形的高度(以像素为单位)。

lpBits

指向图像位的指针,图像位存储为字节数组。

lpbmi

指向包含有关DIB信息的BITMAPINFO结构的指针。

iUsage

指定是否提供了BITMAPINFO结构的bmiColors成员。如果提供,则指定bmiColors是否包含显式的红色,绿色,蓝色(RGB)值或索引。iUsage参数必须是以下值之一:

DIB_PAL_COLORS

该数组包含进入源设备上下文的逻辑调色板的16位索引。

DIB_RGB_COLORS

颜色表包含文字RGB值。

rop

光栅操作代码,指定如何将源像素,目标设备上下文的当前画笔和目标像素组合在一起以形成新图像。

以MFC(提示:MFC已经被市场淘汰,这里只做演示用。除非维护老项目,否则不应选用MFC。)为例,在视图类(负责前台响应)的OnDraw函数中,使用StretchDIBits显示位图,则每次窗口被重绘时就会显示指定的位图:

void CTestView::OnDraw(CDC* pDC) {

CTESTDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

if (!pDoc) return;

// TODO: add draw code for native data hereif (nullptr == lpBitsInfo) return;LPVOID lpBits = (LPVOID)&lpBitsInfo->bmiColors[lpBitsInfo->bmiHeader.biClrUsed];StretchDIBits(pDC->GetSafeHdc(),0, 0, lpBitsInfo->bmiHeader.biWidth, lpBitsInfo->bmiHeader.biHeight,0, 0, lpBitsInfo->bmiHeader.biWidth, lpBitsInfo->bmiHeader.biHeight,lpBits, lpBitsInfo, DIB_RGB_COLORS, SRCCOPY);

}

其中

<minwindef.h>:

typedef void farLPVOID;

<wingdi.h>:

#define DIB_RGB_COLORS 0 /color table in RGBs/

#define DIB_PAL_COLORS 1 /color table in palette indices/

#define SRCCOPY (DWORD)0x00CC0020 /dest = source */

此代码中,原图像与目标显示区域是按照1:1的比例来显示的,无放缩。

lpbmi参数这里为lpBitsInfo,它是一个BITMAPINFO结构的指针,此结构定义于<wingdi.h>:

typedef struct tagBITMAPINFO {

BITMAPINFOHEADER bmiHeader;

RGBQUAD bmiColors[1];

} BITMAPINFO, FAR *LPBITMAPINFO, *PBITMAPINFO;

第一个成员bmiHeader便是BMP文件的信息头;第二个成员的长度通常远远大于1,包含调色板(如果有的话)和位图数据。

lpBits参数这里为lpBits,是一个void型指针。这里指向调色板(如果有)之后的位图数据的首个字节。

处理图像内容时要注意:如要访问像素点<i,j>的数据,则其在内存中的位置应当为:

S+L_LINE (h-1-i)+j

其中S为位图数据实际的开始位置,即上文的lpBits;L_LINE为一行的字节数:

L_LINE=(wb+31)/32×4

w为图像的宽度,b为位图的位数(1、4、8、24)。

可见,L_LINE是对齐之后的长度。wb是一整行像素占用的位数。而当32 | wb时,上式也可写为

L_LINE=(wb+31)/32×4=wb/32×4=8wb

此时一行的长度已经按4字节对齐。多出来的31会被整除运算截断。容易看出:已对齐的情况下,一旦一行的数据再多出1位,那么就要多占用4个字节。

如果需要遍历一个BMP图像的每一个像素点<i,j>,那么循环应当这样写:

for (LONG i = 0; i < h; ++i)

for (LONG j = 0; j < w; ++j) {

}

i对应行编号,j对应列编号。

注意:一幅w×h的图像,其水平像素(列数)和垂直像素(行数)的取值范围分别是[0, w-1]和[0, h-1]之间的整数。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。