关于bmp、jpg格式图片的解码

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

目录

文章目录

前言

一、为什么要解码?

二、bmp图片

1.bmp图片信息

2.获取图片信息

三、jpg图片 

总结


前言

        在学习C语言、文件IO、数据结构之后做了一个6818的开发板项目,自觉想要记录一下,本该将整个项目整理好发布的,由于当时着急提交查验,代码并未优化,继而现在将在做项目的时候遇到的自认为较关键的图片格式解码整理一下。


一、为什么要解码?

        想要将图片写入或者映射到开发板设备上,就必须知道图片的像素信息,但图片的信息是一整个合集,各种信息都集中在里面,包括图片的文件类型、图片大小、像素等等信息,而我们只需要像素信息,将之写入/映射到设备,这就需要解码。

二、bmp图片

1.bmp图片信息

  bmp图片信息前面14个字节文件信息 Bitmap File Header,包含文件类型、文件大小等信息,接下来40个字节位图信息Bitmap Information Header,这部分包含位图的宽高、位图数据大小等很多信息,接下来才是像素信息,而我们读取的就是这部分,想要读取这部分信息就要:

                                        Bitmap File Header
                位置      大小(byte)                 描述
00 0 2 头文件字段
02 2 4 整个.bmp文件大小
06 6 2 预留字段,通常为0
08 8 2
0A 10 4 图片信息的开始位置
                                Bitmap Information Header
                 位置 尺寸 描述
0E 14 4 DIB  header的大小通常为40byte即0x28
12 18 4 图像宽度
16 22 4 图像高度
1A 26 2 色彩平面的数量必须为1
1C 28 2 每像素用多少bit来表示
1E 30 4 采用何种压缩方式,通常不压缩,即BI_RGB,对应值为0
22 34 4

图片大小(原始位图数据的大小)

对于不压缩的图片,通常表示为0

26 38 4

横向分辨率(像素/米)

2A 42 4 纵向分辨率(像素/米)
2E 46 4

调色板中颜色数量

通常为0(不表示没有颜色)

32 50 4

重要颜色的数量(通常被忽略)

通常为0(表示每种颜色都重要)

2.获取图片信息

(1)获取图片的宽度和高度

   以下是图片文件信息结构体

// 前14个字节
struct bitmap_header
{
	int16_t type;
	int32_t size; // 图像文件大小
	int16_t reserved1;
	int16_t reserved2;
	int32_t offbits; // bmp图像数据偏移量
}__attribute__((packed));

// 14-54个字节
struct bitmap_info
{
	int32_t size;   // 本结构大小	
	int32_t width;  // 图像宽
	int32_t height; // 图像高
	int16_t planes;

	int16_t bit_count; // 色深
	int32_t compression;
	int32_t size_img; // bmp数据大小,必须是4的整数倍
	int32_t X_pel;
	int32_t Y_pel;
	int32_t clrused;
	int32_t clrImportant;
}__attribute__((packed));

故而要获取图片大小为:

    int fd = open("dd.bmp", O_RDONLY);

	// 读取BMP格式头,获取图片信息
	struct bitmap_header header;
	struct bitmap_info info;
	

    // 读取文件信息到结构体
	read(fd, &header, sizeof(header));
	read(fd, &info, sizeof(info));

    // 输出图片相关信息
    printf("图片大小: %d\n", header.size);
    printf("图片尺寸: %d×%d\n", info.width, info.height);

(2)获取图片的像素信息

    // 打开bmp图片
    int fd_pic = open(addr_bmp, O_RDONLY);
    if (fd_pic < 0)
    {
        perror("open bmp faile");
        return -1;
    }

    // 图片偏移54位
    lseek(fd_pic, 54, SEEK_SET);

    // 获取图片像素信息
    char rgb[480 * 800 * 3] = {0};

    read(fd_pic, rgb, sizeof(rgb));
 

三、jpg图片 

关于jpg(jpeg)图片的解码是使用第三方库来实现的,因此编译时要带上库

jpeg第三方库下载Directory Listing of /files (ijg.org)

解码过程如下:

#include 
#include "jpeglib.h"
#include 
#include 
struct my_error_mgr
{
  struct jpeg_error_mgr pub; /* "public" fields */
  jmp_buf setjmp_buffer;     /* for return to caller */
};

typedef struct my_error_mgr *my_error_ptr;

METHODDEF(void)
my_error_exit(j_common_ptr cinfo)
{

  my_error_ptr myerr = (my_error_ptr)cinfo->err;

  (*cinfo->err->output_message)(cinfo);

  longjmp(myerr->setjmp_buffer, 1);
}
//解码jpge
int read_JPEG_file(void *lcd_addr, char *filename)
{

  // 定义一个jpeg解码对象
  struct jpeg_decompress_struct cinfo;

  // 定义一个jpeg错误对象
  struct my_error_mgr jerr;
  FILE *infile;      /* 源文件 */
  JSAMPARRAY buffer; /* 输出行缓存 */
  int row_stride;    /* 输出行大小 */

  
  // 打开需要解码的jpeg文件
  if ((infile = fopen(filename, "rb")) == NULL)
  {
    fprintf(stderr, "can't open %s\n", filename);
    return 0;
  }

  /* Step 1: 初始化解码对象*/
  /* 设置jpeg出错处理函数*/
  cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = my_error_exit;
  if (setjmp(jerr.setjmp_buffer))
  {
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    return 0;
  }

  /*初始化解码对象函数*/
  jpeg_create_decompress(&cinfo);

  /* Step 2: 关联文件与解码对象 */
  jpeg_stdio_src(&cinfo, infile);

  /* Step 3: 读取jpeg文件头*/
  (void)jpeg_read_header(&cinfo, TRUE);

  /* Step 5: 开始解码 */
  (void)jpeg_start_decompress(&cinfo);

  // 保存图像的宽度与高度
  int width = cinfo.output_width;
  int height = cinfo.output_height;

  row_stride = cinfo.output_width * cinfo.output_components;

  /* 分配一行的RGB数据空间 */
  buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1);

  // buffer 输出行缓存,是一个二维数组

  // 把解码后的数据输出到argb数组中
  char argb[width * height * 4];
  int y = 0;
  // cinfo.output_scanline 解码的当前行  cinfo.output_height 图像的总行数
  while (cinfo.output_scanline < cinfo.output_height)
  {

    (void)jpeg_read_scanlines(&cinfo, buffer, 1);

    char *p = buffer[0]; // 指向解码后的RGB 数据
    for (int x = 0; x < width; x++)
    {
      argb[y * width * 4 + 2 + x * 4] = buffer[0][0 + x * 3]; // R
      argb[y * width * 4 + 1 + x * 4] = buffer[0][1 + x * 3]; // G
      argb[y * width * 4 + 0 + x * 4] = buffer[0][2 + x * 3]; // B
      argb[y * width * 4 + 3 + x * 4] = 0;
    }
    y++;
  }

  // 把LCD 转换为二维数组
  int(*lcd)[800] = lcd_addr;

  // 把argb 转换为二维数组
  int(*color)[width] = (void *)argb;

  for (int y = 0; y < height; y++)
  {
    for (int x = 0; x < width; x++)
    {
      if (y < 480 && x < 800) // 限制显示范围
      {
        lcd[y][x] = color[y][x]; // 把小图的对应像素点放入屏幕的对应位置
      }
    }
  }

  /* Step 7: 结束解码 */
  (void)jpeg_finish_decompress(&cinfo);
  // 销毁解码对象
  jpeg_destroy_decompress(&cinfo);
  // 关闭文件
  fclose(infile);

  return 1;
}

void *inin_lcd()
{
    // 1.打开LCD 设备
    int lcd = open("/dev/fb0", O_RDWR);
    if (lcd == -1)
    {
        perror("打开 /dev/fb0 失败");
        exit(0);
    }

    // 定义LCD 设备信息结构体
    struct fb_var_screeninfo varinfo;
    // FBIOGET_VSCREENINFO 获取屏幕参数命令,成功后会把屏幕的所有信息放入varinfo结构体中
    if (ioctl(lcd, FBIOGET_VSCREENINFO, &varinfo) != 0)
    {
        perror("获取LCD设备可变属性信息失败");
        return 0;
    }

    // 根据获取的参数映射 映射的length 多少个字节?? varinfo.xres*varinfo.yres*varinfo.bits_per_pixel/4
    // 32/8 = 4
    void *lcd_p = mmap(NULL, varinfo.xres * varinfo.yres * varinfo.bits_per_pixel / 8, PROT_READ | PROT_WRITE,
                       MAP_SHARED, lcd, 0);

    close(lcd);

    // 返回映射地址
    return lcd_p;
}

int main()
{
    //初始化lcd设备
    void *lcd = inin_lcd();

    read_JPEG_file(lcd, adr_jpg);

    return;
}

编译时注意,以下仅供参考

arm-linux-gcc main.c -o main -I /home/gec/jpeglib/include  -L /home/gec/jpeglib/lib -ljpeg
参数解析:
-I/home/gec/jpeglib/include #指定头文件所在路径
-L/home/gec/jpeglib/lib #指定库文件所在路径
-ljpeg #指定库的名称


总结

以上就是关于这两种图片格式的解压总结,如有不足请指教,本人初学嵌入式,写文章一是为了总结回顾,二是为了方便日后翻阅查看。

你可能感兴趣的:(c语言,单片机,嵌入式硬件)