一、实验目的
1.理解图像文件的基本组成。
2.掌握结构体作为复杂数据对象的用法。进一步熟悉由问题到程序的解决方案,并掌握
编程细节:如内存分配、倒序读写、字节序、文件读写过程等。
二、实验原理
1、BMP概述
- 位图文件(Bitmap-File,BMP)格式是Windows采用的图像文件存储格式,其位图文件默认的文件扩展名是bmp或者dib。
- BMP格式分析如下:
数据段名称 | 含义 | 大小 |
---|---|---|
位图文件头 (BITMAPFFILEHEADER) | 提供文件类型、大小等信息 | 14字节 |
位图信息头(BITMAPINFOHEADER) | 提供图像的尺寸、位面数、压缩方式、颜色索引等信息 | 40字节 |
调色板(Palette) | 可选。实际上为一个数组,包含元素与位图颜色数相同 | 由颜色索引数决定 |
位图数据(ImageData) | 图像数据 | 由图像尺寸决定 |
(1)位图文件头
typedef struct tagBITMAPFILEHEADER {
WORD bfType; /* 说明文件的类型 */
DWORD bfSize; /* 说明文件的大小,用字节为单位 */
WORD bfReserved1; /* 保留,设置为 0 */
WORD bfReserved2; /* 保留,设置为 0 */
DWORD bfOffBits; /* 说明从 BITMAPFILEHEADER 结构开始到实际的图像数
据之间的字节偏移量 */
} BITMAPFILEHEADER;
(2)位图信息头
typedef struct tagBITMAPINFOHEADER {
DWORD biSize; /* 说明结构体所需字节数 */
LONG biWidth; /* 以像素为单位说明图像的宽度 */
LONG biHeight; /* 以像素为单位说明图像的高速 */
WORD biPlanes; /* 说明位面数,必须为 1 */
WORD biBitCount; /* 说明位数/像素,1、2、4、8、24 */
DWORD biCompression; /* 说明图像是否压缩及压缩类型 BI_RGB,BI_RLE8,BI_RLE4,
BI_BITFIELDS */
DWORD biSizeImage; /* 以字节为单位说明图像大小,必须是 4 的整数倍*/
LONG biXPelsPerMeter; /*目标设备的水平分辨率,像素/米 */
LONG biYPelsPerMeter; /*目标设备的垂直分辨率,像素/米 */
DWORD biClrUsed; /* 说明图像实际用到的颜色数,如果为 0,则颜色数为 2 的 biBitCount
次方 */
DWORD biClrImportant; /*说明对图像显示有重要影响的颜色索引的数目,如果是 0,表
示都重要。*/
} BITMAPINFOHEADER;
(3)调色板
typedef struct tagRGBQUAD {
BYTE rgbBlue; /*指定蓝色分量*/
BYTE rgbGreen; /*指定绿色分量*/
BYTE rgbRed; /*指定红色分量*/
BYTE rgbReserved; /*保留,指定为 0*/
} RGBQUAD;
(4)位图数据
- 图像数据字节阵列在调色板之后。
- 若有调色板,图像数据就是该像素颜色在调色板中的索引值(逻辑色);若无调色板,如真彩色图,图像数据就是实际的 R、G、B值。
- 图像的每一扫描行由表示图像像素的连续的字节组成,每一行的字节数取决于图像的颜色数目和用像素表示的图像宽度。规定每一扫描行的字节数必须是 4 的整倍数。
- 扫描行由底向上存储的,即阵列中的第一个字节表示位图左下角的像素,最后一个字节表示位图右上角的像素
2、从BMP中提取RGB数据流程
注意,根据每像素位数的不同,具体操作也不同:
像素位数 | 具体操作 |
---|---|
8bit以下 | 构造调色板,位与移位取像素数据查调色板,写RGB缓冲区 |
16bit | 位与移位取像素数据转换为8bit/彩色分量,写RGB缓冲区 |
24/32bit | 直接取像素数据,写RGB缓冲区 |
3、RGB转YUV的实现
- Y=0.30R+0.59G+0.11B
- U=0.493(B-Y)
- V=0.877(R-Y)
- 具体代码见我的文章彩色空间转换
三、实验步骤
1.将图片转换成相同大小且宽高均为4的整数倍的BMP文件(至少五张)
2.根据需要创建头文件与源文件
3.分析BMP文件数据中的结构,读取BMP文件,提取RGB数据写入缓冲区
4.实现RGB到YUV的转换
5.生成yuvout.yuv文件,并用YUVviewerPlus查看
6.关闭文件,释放缓冲区
四、实验代码
1.bmp2yuv.h
#include <stdio.h>
#include<math.h>
#include <malloc.h>
#include<windows.h>
void BMP2RGB(FILE* pFile, BITMAPFILEHEADER& file_h, BITMAPINFOHEADER& info_h, unsigned char* rgbDataOut);
int RGB2YUV(int x_dim, int y_dim, void* bmp, void* y_out, void* u_out, void* v_out, int flip);
bool MakePalette(FILE* pFile, BITMAPFILEHEADER& file_h, BITMAPINFOHEADER& info_h, RGBQUAD* pRGB_out);
bool WriteYUV(unsigned char* Y, unsigned char* U, unsigned char* V, unsigned long size, FILE* outFile);
void InitLookupTable();//部分查找表
2.bmp2yuv.cpp
(1)准备工作
#include <stdio.h>
#include<math.h>
#include <malloc.h>
#include<windows.h>
#include "bmp2yuv.h"
#include "stdlib.h"
#define u_int8_t unsigned __int8
#define u_int unsigned __int32
#define u_int32_t unsigned __int32
static float RGBYUV02990[256], RGBYUV05870[256], RGBYUV01140[256];
static float RGBYUV01684[256], RGBYUV03316[256];
static float RGBYUV04187[256], RGBYUV00813[256];
(2)BMP文件中提取RGB数据
void BMP2RGB(FILE* pFile, BITMAPFILEHEADER& file_h, BITMAPINFOHEADER& info_h, unsigned char* rgbDataOut)
{
u_int32_t w, h, width, height;
u_int i, j;
unsigned char* dataBuf, * Data;
//判断像素的实际点阵数,保证图像大小为4字节的整数倍
if (((info_h.biWidth / 8 * info_h.biBitCount) % 4) == 0)
w = info_h.biWidth;
else
w = (info_h.biWidth * info_h.biBitCount + 31) / 32 * 4;
if ((info_h.biHeight % 2) == 0)
h = info_h.biHeight;
else
h = info_h.biHeight + 1;
width = w / 8 * info_h.biBitCount;//width为实际一行的字节数
heig