Linux应用程--BMP与JPG格式图片解码
文章摘要: 本文主要描述了BMP/JPG解码及显示的示例。
操作系统: Debian
BMP数据结构:
// BMP文件头(54字节),其中文件头14字节,图像信息40字节
#pragma pack(1) //设置内存按照1个字节对齐
typedef struct
{
char bfType[2]; // 00 文件类型: 固定为BM
uint32_t bfSize; // 02 文件大小:单位字节
uint16_t bfReserved1; // 06 保留字段
uint16_t bfReserved2; // 08 保留字段
uint32_t bfOffBits; // 10 数据偏移
uint32_t biSize; // 14 信息结构长度
uint32_t biWidth; // 18 图像宽度
uint32_t biHeight; // 22 图像高度
uint16_t biPlanes; // 26 颜色面板数(总是为1)
uint16_t biBitCount; // 28 像素位数
uint32_t biCompression; // 30 压缩格式
uint32_t biSizeImage; // 34 图像内容大小
uint32_t biXPelsPerMeter; // 38 水平分辨率 像素/米
uint32_t biYPelsPerMeter; // 42 竖直分辨率 像素/米
uint32_t biClrUsed; // 46 调色板数量
uint32_t biClrImportant; // 50 关键色彩数
} BITMAPINFOHEADER, bmp_info_t;
#pragma pack()
BMP文件解析:
bmp_info_t* bmp_info = (void*)&buff[0];
// 正常情况是读出54个字节
fread(buff, 54, 1, fp);
dump_data(buff, 54);
printf("文件类型: %c%c\r\n", bmp_info->bfType[0], bmp_info->bfType[1]);
printf("文件大小: %d 字节\r\n", bmp_info->bfSize);
printf("数据偏移: %d\r\n", bmp_info->bfOffBits);
printf("信息长度: %d\r\n", bmp_info->biSize);
printf("图像尺寸: %dx%d\r\n", bmp_info->biWidth, bmp_info->biHeight);
printf("像素位数: %d bits\r\n", bmp_info->biBitCount);
printf("压缩格式: %d\r\n", bmp_info->biCompression);
printf("图像内容: %d 字节\r\n", bmp_info->biSizeImage);
printf("分 辨 率: %d,%d\r\n", bmp_info->biXPelsPerMeter, bmp_info->biXPelsPerMeter);
printf("调色板数: %d\r\n\r\n", bmp_info->biClrUsed);
// 如果文件头大于54个字节,需要读出其他数据
if(bmp_info->bfOffBits > 54)
{
fread(buff, bmp_info->bfOffBits - 54, 1, fp);
}
// 申请读取一行数据需要的内存
int line_size = bmp_info->biWidth * bmp_info->biBitCount / 8;
// BMP文件和每行长度必须以4字节对齐,不足部分填充0,所以必须全部读出来
// 否则显示会错位
line_size = (line_size + 3 )/ 4 * 4;
char *pline = malloc(line_size);
if (pline == NULL)
{
perror("内存分配失败");
exit(-2);
}
//----------------------------
int xcnt;
int ycnt;
int offset; // 偏移
char *p2;
char *p3;
// 屏幕和图像尺寸取最小值
xcnt = (bmp_info->biWidth < vinfo.xres)?bmp_info->biWidth :vinfo.xres;
ycnt = (bmp_info->biHeight < vinfo.yres)?bmp_info->biHeight:vinfo.yres;
while (ycnt--)
{
// 从文件中读一行数据出来
fread((char *)pline, 1, line_size, fp);
// 计算偏移值
offset = (ycnt) * finfo.line_length;
p2 = (char *)(pfb + offset);
p3 = pline;
for (j = 0; j < xcnt; j++)
{
*p2++ = *p3++; // 先按24bit显示
*p2++ = *p3++;
*p2++ = *p3++;
*p2++ = 0;
}
}
JPG图像解码:
需要添加libjpeg-dev支持;
编译选项:-ljpeg
// 指定图片的路径就可以调用这个jpg的解析
// output_width: 图像的输出宽度
// output_height: 图像的输出高度
// output_component: 每个像素的分量数,也即字节数(3或4字节)
int jpg_display(char *path)
{
// 1. 分配并初始化一个jpeg解压对象
struct jpeg_decompress_struct dinfo;
struct jpeg_error_mgr jerr; // 错误变量
dinfo.err = jpeg_std_error(&jerr); //
jpeg_create_decompress(&dinfo); // 初始化
// 2. 打开JPG图像文件
FILE *infile;
infile = fopen(path, "r");
if (infile == NULL)
{
perror("fopen error");
return -1;
}
jpeg_stdio_src(&dinfo, infile); //为解压对象dinfo指定要解压的文件
// 3. 获取图像信息
jpeg_read_header(&dinfo, TRUE);
//-------------------------------------------------
// 4. 设置jpeg解压缩对象dinfo的一些参数,可采用默认参数
//-------------------------------------------------
// 5. 解码文件头
jpeg_start_decompress(&dinfo);
printf("图像信息: %dx%d %d\r\n", dinfo.output_width, dinfo.output_height, dinfo.output_components);
//-----------------------------------------------------
// 6. 读取一行或者多行扫描线数据并处理
// 分配内存以便读入数据
unsigned char *line_buff = malloc(dinfo.output_width * dinfo.output_components);
if(line_buff == NULL)
{
perror("malloc failed");
exit(1);
}
while (dinfo.output_scanline < dinfo.output_height)
{
// 读取一行数据并解码(output_scanlinee表示当前行数,会自动增加)
jpeg_read_scanlines(&dinfo, &line_buff, 1);
// 此处加入数据后续处理
// BMP的顺序为BGR, 显示器顺序为BGRA, JPG的顺序为RGB
// 如果直接输出为图片,则显示为上下翻转且颜色也不对(BMP本身的问题)
}
// 完成解压过程
jpeg_finish_decompress(&dinfo);
// 释放内容
jpeg_destroy_decompress(&dinfo);
return 0;
}
调整图像(输出BMP格式文件):
int sp;
int offset;
unsigned char *file_buff = malloc(line_size * dinfo.output_height);
unsigned char *line_buff = malloc(line_size);
if((file_buff == NULL) || (line_buff == NULL))
{
exit(1);
}
while (dinfo.output_scanline < dinfo.output_height)
{
offset = (dinfo.output_height - dinfo.output_scanline - 1) * line_size;
unsigned char *p = file_buff + offset;
jpeg_read_scanlines(&dinfo, &line_buff, 1);
sp = 0;
for (int i = 0; i < dinfo.output_width; i++)
{
*p++ = line_buff[sp+2];
*p++ = line_buff[sp+1];
*p++ = line_buff[sp];
sp += dinfo.output_components;
}
}
FILE* bmp_fp = bmp_file_init(&dinfo);
fwrite(file_buff,1,dinfo.output_width*dinfo.output_components*dinfo.output_height,bmp_fp);
fclose(bmp_fp);
BMP文件头处理:
FILE* bmp_file_init(struct jpeg_decompress_struct* j)
{
FILE *fp;
bmp_info_t bmp_info;
long int img_size = j->output_width * j->output_height * j->output_components;
bmp_info.bfType[0] = 'B';
bmp_info.bfType[1] = 'M';
bmp_info.bfSize = img_size + 54; // 文件大小
bmp_info.bfReserved1 = 0; // 06 保留
bmp_info.bfReserved2 = 0; // 08 保留
bmp_info.bfOffBits = 54; // 10 数据偏移(一般都为54)
bmp_info.biSize = 40; // 14 信息结构长度
bmp_info.biWidth = j->output_width; // 18 图像宽度
bmp_info.biHeight = j->output_height; // 22 图像高度
bmp_info.biPlanes = 1; // 26 颜色面板数(总是为1)
bmp_info.biBitCount = 24; // 28 像素位数
bmp_info.biCompression = 0; // 30 压缩格式
bmp_info.biSizeImage = img_size; // 34 图像内容大小
bmp_info.biXPelsPerMeter = 1024; // 38 水平分辨率 像素/米
bmp_info.biYPelsPerMeter = 1024; // 42 竖直分辨率 像素/米
bmp_info.biClrUsed = 0; // 46 调色板数量
bmp_info.biClrImportant = 0; // 50 关键色彩数
fp = fopen("output.bmp", "wb");
if(fp == NULL)
{
perror("Open Bmp File Failed");
exit(-1);
}
fwrite(&bmp_info,1,54,fp);
return fp;
}