首页 > 编程语言 >Jpeg算法压缩

Jpeg算法压缩

时间:2024-05-24 21:07:24浏览次数:33  
标签:Jpeg buffer 压缩 JPEG width 算法 cinfo jpeg image

Jpeg算法压缩

JPEG格式图片文件背后的算法:

  1. 色彩空间转换(Color Space Conversion"),将RGB转换为YUV色彩空间,YUV的数据更好处理
  2. 色度缩减采样(Chromenance Downsampling),将蓝红色度层的“分辨率”变小,因为人眼对颜色不敏感
  3. 离散余弦变换(Discrete Cosine Transform),找出人眼不敏感的高频信息
  4. 量化(Quantization),删除人眼不敏感的高频信息
  5. 游程编码与霍夫曼编码(Run-length Encoding & Huffman Encoding),通用数据压缩

arm-linux环境下的安装

  1. 前往源代码管理网站下载安装
    image-20240513185418959

  2. 解压安装

    • 配置+编译+安装

    • ./configure

      ./configure --prefix=home/hemy/workspace/libjpeg_dir/libjpeg --host=arm-linux
      

      image-20240513190232057

      image-20240513190402800

    • make

      命令行输入 make ,执行脚本文件

      make
      
    • make install

      编译通过后,输入 make install 执行安装

      make install
      

      image-20240513191229307

  3. 安装完成后拷贝libjpegd的头文件和库文件

    image-20240513191633488
    1. include文件夹和lib文件夹与自己的工程文件放入同一个路径,方便工程维护

jpeglib库的使用

阅读README 阅读install.txt libjpeg.txt example.c

image-20240513192039294

jpg图片解压流程

  1. 创建解码对象,并且对解码对象进行初始化,另外需要创建错误处理对象,并和解码对象进行关联。

    image-20240513163842780

  2. 打开待解码的jpg图片,使用fopen的时候需要添加选项”b”,以二进制方式打开文件!

    image-20240513163912560

  3. 读取待解码图片的文件头,并把图像信息和解码对象进行关联,通过解码对象对jpg图片进行解码

    image-20240513163927330

  4. 可以选择设置解码参数,如果打算以默认参数对jpg图片进行解码,则可以省略该步骤!

    image-20240513163935661

  5. 开始对jpg图片进行解码,调用函数之后开始解码,可以得到图像宽、图像高等信息!

    image-20240513163942543

  6. 开始设计一个循环,在循环中每次读取1行的图像数据,并写入到LCD中,注意:转换算法需要用户自己设计。

    image-20240513163950339

  7. 在所有的图像数据都已经解码完成后,则调用函数完成解码即可,然后释放相关资源!

    image-20240513163957905

  8. 释放解码对象

    image-20240513164006794

jpg图片压缩流程

  1. 分配和初始化JPEG压缩对象

    • 使用jpeg_compress_struct结构体保存JPEG压缩所需的信息。
    • 初始化一个jpeg_error_mgr结构体,用于处理错误信息。
    • 调用jpeg_create_compress函数创建并初始化压缩对象。
  2. 指定压缩后数据的输出目标

    • 打开目标文件,并将文件流赋值给jpeg_stdio_dest函数,以便将压缩后的数据写入文件。
  3. 设置压缩参数

    • 设置图像的宽度、高度、颜色通道数和颜色空间等参数。
    • 使用jpeg_set_defaults函数设置默认压缩参数。
    • 使用jpeg_set_quality函数设置压缩质量。
  4. 开始压缩

    • 调用jpeg_start_compress函数开始压缩过程。
  5. 逐行写入图像数据

    • 使用jpeg_write_scanlines函数逐行写入图像数据。
  6. 完成压缩

    • 在写入所有图像数据后,调用jpeg_finish_compress函数完成压缩过程。
  7. 释放资源

    • 调用jpeg_destroy_compress函数释放JPEG压缩对象及其相关资源。

以上是使用libjpeg库进行JPEG压缩的基本流程。在实际应用中,可以根据需要调整压缩参数以及处理压缩过程中可能出现的错误情况。

代码实现

  • 解码JPEG文件并在LCD屏幕的指定位置显示
//解码JPEG文件并在LCD屏幕的指定位置显示。
/***************************************************************************************
*
*   @名称   : read_JPEG_file
*   @描述   : 解码JPEG文件并在LCD屏幕的指定位置显示。
*   @参数   : 
*             - filename: 指向JPEG文件路径的字符串指针。
*             - start_x: 图像开始显示的LCD屏幕上的x坐标。
*             - start_y: 图像开始显示的LCD屏幕上的y坐标。
*   @返回值 : 如果图像成功显示,则返回1;如果发生错误,则返回0。
*   @日期   : 2024/05/17
*   @作者   : sanjiudemiao@163.com
*   @版本   : 
*             1. 2024/05/18 - 优化接口,添加了额外的参数,以更好地控制图像定位。
*             
*   @注意   : 此函数设计用于在LCD屏幕上显示分辨率为800x480以内的JPEG图像。
*             如果JPEG图像恰好是800x480,必须将'start_x'和'start_y'设置为0,以避免段错误。
*             函数假设LCD屏幕缓冲区'lcd_mp'已正确初始化并可访问。
*
***************************************************************************************/
int read_JPEG_file (int *lcd_mp,char * filename,int start_x,int start_y)
{
  //参数有效性分析,判断LCD内存映射的地址是否有效
	if(NULL == lcd_mp)
	{
		printf("lcd mp is invaild\n");
		return 0;
	}
  /* This struct contains the JPEG decompression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   */
  struct jpeg_decompress_struct cinfo;
  /* We use our private extension JPEG error handler.
   * Note that this struct must live as long as the main JPEG parameter
   * struct, to avoid dangling-pointer problems.
   */
  struct jpeg_error_mgr jerr;
  /* More stuff */
  FILE * infile;			/* source file */
  unsigned char * buffer;		/* Output row buffer */
  int row_stride;			/* physical row width in output buffer */

  /* In this example we want to open the input file before doing anything else,
   * so that the setjmp() error recovery below can assume the file is open.
   * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
   * requires it in order to read binary files.
   */

  if ((infile = fopen(filename, "rb")) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    return 0;
  }

  /* Step 1: allocate and initialize JPEG decompression object */

  /* We set up the normal JPEG error routines, then override error_exit. */
  cinfo.err = jpeg_std_error(&jerr);

  /* Now we can initialize the JPEG decompression object. */
  jpeg_create_decompress(&cinfo);

  /* Step 2: specify data source (eg, a file) */

  jpeg_stdio_src(&cinfo, infile);

  /* Step 3: read file parameters with jpeg_read_header() */

  (void) jpeg_read_header(&cinfo, TRUE);
  /* We can ignore the return value from jpeg_read_header since
   *   (a) suspension is not possible with the stdio data source, and
   *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
   * See libjpeg.txt for more info.
   */

  /* Step 4: set parameters for decompression */

  /* In this example, we don't need to change any of the defaults set by
   * jpeg_read_header(), so we do nothing here.
   */

  /* Step 5: Start decompressor */

  (void) jpeg_start_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* We may need to do some setup of our own at this point before reading
   * the data.  After jpeg_start_decompress() we have the correct scaled
   * output image dimensions available, as well as the output colormap
   * if we asked for color quantization.
   * In this example, we need to make an output work buffer of the right size.
   */ 
  /* JSAMPLEs per row in output buffer */
  row_stride = cinfo.output_width * cinfo.output_components;  //计算一行的大小

  /* Make a one-row-high sample array that will go away when done with image */
  buffer = calloc(1,row_stride);

  /* Step 6: while (scan lines remain to be read) */
  /*           jpeg_read_scanlines(...); */

  /* Here we use the library's state variable cinfo.output_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   */
  int data = 0;

  
	if(cinfo.output_width > 800 || cinfo.output_height > 480)
	{
	printf("[%s] width or height is too long\n", filename);
	return 0;
	}

  while (cinfo.output_scanline < cinfo.output_height) 
  {
    /* jpeg_read_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could ask for
     * more than one scanline at a time if that's more convenient.
     */
    (void) jpeg_read_scanlines(&cinfo, &buffer, 1); //从上到下,从左到右  RGB RGB RGB RGB 
  	
  	for (int i = 0; i < cinfo.output_width; ++i)  //012 345
  	{
  		data |= buffer[3*i]<<16;	//R
		  data |= buffer[3*i+1]<<8;	//G
		  data |= buffer[3*i+2];  	//B 

		//把像素点写入到LCD的指定位置
		lcd_mp[800*start_y + start_x + 800*(cinfo.output_scanline-1) + i] = data;

		data = 0;
  	}
  
  }

  /* Step 7: Finish decompression */

  (void) jpeg_finish_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* Step 8: Release JPEG decompression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_decompress(&cinfo);

  /* After finish_decompress, we can close the input file.
   * Here we postpone it until after no more JPEG errors are possible,
   * so as to simplify the setjmp error logic above.  (Actually, I don't
   * think that jpeg_destroy can do an error exit, but why assume anything...)
   */
  fclose(infile);

  /* At this point you may want to check to see whether any corrupt-data
   * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
   */

  /* And we're done! */
  return 1;
}

  • 将BMP图像文件转换为JPEG格式并保存到指定文件。
//将BMP图像文件转换为JPEG格式并保存到指定文件。
/***************************************************************************************
*
*   @名称   : write_JPEG_file
*   @描述   : 将BMP图像文件转换为JPEG格式并保存到指定文件。
*   @参数   : 
*             - filename: 指向将要保存JPEG文件路径的字符串指针。
*             - quality: 整数,指定JPEG图像的质量,范围从0(最差质量,最大压缩)到100(最佳质量,最小压缩)。
*   @返回值 : 如果转换成功并且文件被成功写入,则返回1;如果发生错误,则返回0。
*   @日期   : 2024/05/22
*   @作者   : sanjiudemiao@163.com
*   @版本   : 
*             1. 2024/05/22 - 初始版本,实现了从BMP到JPEG的基本转换功能。
*             
*   @注意   : 此函数使用libjpeg库进行JPEG压缩。确保在调用此函数之前已经安装并配置了libjpeg。
*             函数不会对输入的BMP图像进行缩放或裁剪,所以输入图像的尺寸应该与最终JPEG图像的尺寸相匹配。
*             质量参数会影响输出文件的大小和图像质量。
*
***************************************************************************************/
int write_JPEG_file (char * filename, int quality)
{
//unsigned char  * image_buffer;	/* Points to large array of R,G,B-order data */
 int image_height;	/* Number of rows in image */
 int image_width;		/* Number of columns in image */

//1.打开待显示的BMP图像  fopen
  FILE * bmp_fp = fopen("10.bmp","rb"); //转换10.bmp
  if (NULL == bmp_fp)
  {
    return -1;
  }

//2.读取BMP文件的图像信息,获取BMP的宽和高
	BITMAPFILEHEADER *fileinfo = malloc(14);
	BITMAPINFOHEADER *headerinfo = malloc (40);
	
    
//读取bmp的头信息
  fread(fileinfo,14,1,bmp_fp); //读取14字节
  fread(headerinfo,40,1,bmp_fp); //读取40字节
  printf("bmp bfSize = %d\n",fileinfo->bfSize);
  printf("bmp width = %d,height = %d\n",headerinfo->biWidth,headerinfo->biHeight);
//备份宽和高
	int width=headerinfo->biWidth;
	int height=headerinfo->biHeight;
  int width_th=(width*24/8)+((4-(width*24/8)%4)%4);  //字节补齐

  image_width=width;
  image_height=height;

//读取RGB信息
// 为 image_buffer 分配内存
unsigned char *image_buffer = malloc(width * height * 3);
unsigned char *rgb_buffer = malloc(width_th * height * 3);
printf("----------------------1");
fread(rgb_buffer,1,width_th*height*3,bmp_fp);
printf("----------------------2");

fclose(bmp_fp);

//把BGR转换成RGB
//bmp是小端存储 自下往上
//jpeg是大端存储 自上往下
//FILE * jpeg = fopen("jpegrgb","wb"); 
char temp;
/*************************************************************************/
// for(int i = height-1 ; i >= 0 ; i--)
// {
//     for(int j = 0 ; j < width ; j++)
//     {
//       temp = image_buffer[width_th * i * 3 + j * 3];
//       image_buffer[width_th* i * 3 + j * 3] =image_buffer[width_th * i * 3 + j * 3 + 2];
//       image_buffer[width_th* i * 3 + j * 3 + 2] = temp;
//     }
//     printf("----------------------%d\n",i);
// }
/*****************************bmp格式**************************************/
for(int i = height-1,k=0; i >= 0 ; i--,k++)
{
    for(int j = 0 ; j < width ; j++)
    {
      image_buffer[width * k * 3+ j * 3] = rgb_buffer[width_th * i+ j * 3+2];
      image_buffer[width * k * 3+ j * 3+1] = rgb_buffer[width_th * i+ j * 3+1];
      image_buffer[width * k * 3+ j * 3+2] = rgb_buffer[width_th * i+ j * 3];
    }
    printf("----------------------%d\n",i);
}

    free(fileinfo);
    free(headerinfo);
  /* This struct contains the JPEG compression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   * It is possible to have several such structures, representing multiple
   * compression/decompression processes, in existence at once.  We refer
   * to any one struct (and its associated working data) as a "JPEG object".
   */
  struct jpeg_compress_struct cinfo;
  /* This struct represents a JPEG error handler.  It is declared separately
   * because applications often want to supply a specialized error handler
   * (see the second half of this file for an example).  But here we just
   * take the easy way out and use the standard error handler, which will
   * print a message on stderr and call exit() if compression fails.
   * Note that this struct must live as long as the main JPEG parameter
   * struct, to avoid dangling-pointer problems.
   */
  struct jpeg_error_mgr jerr;
  /* More stuff */
  FILE * outfile =fopen(filename, "wb");		/* target file */
  printf("%s\n",filename);
  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
  int row_stride;		/* physical row width in image buffer */

  /* Step 1: allocate and initialize JPEG compression object */

  /* We have to set up the error handler first, in case the initialization
   * step fails.  (Unlikely, but it could happen if you are out of memory.)
   * This routine fills in the contents of struct jerr, and returns jerr's
   * address which we place into the link field in cinfo.
   */
  cinfo.err = jpeg_std_error(&jerr);
  /* Now we can initialize the JPEG compression object. */
  jpeg_create_compress(&cinfo);

  /* Step 2: specify data destination (eg, a file) */
  /* Note: steps 2 and 3 can be done in either order. */

  /* Here we use the library-supplied code to send compressed data to a
   * stdio stream.  You can also write your own code to do something else.
   * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
   * requires it in order to write binary files.
   */
  if ((outfile) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    return 0;
  }

  jpeg_stdio_dest(&cinfo, outfile);
  printf("jpeg stdio\n");

  /* Step 3: set parameters for compression */

  /* First we supply a description of the input image.
   * Four fields of the cinfo struct must be filled in:
   */
  cinfo.image_width = image_width; 	/* image width and height, in pixels */
  cinfo.image_height = image_height;
  cinfo.input_components = 3;		/* # of color components per pixel */
  cinfo.in_color_space = JCS_RGB; 	/* colorspace of input image */
  /* Now use the library's routine to set default compression parameters.
   * (You must set at least cinfo.in_color_space before calling this,
   * since the defaults depend on the source color space.)
   */
  jpeg_set_defaults(&cinfo);
  /* Now you can set any non-default parameters you wish to.
   * Here we just illustrate the use of quality (quantization table) scaling:
   */
  jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);

  /* Step 4: Start compressor */

  /* TRUE ensures that we will write a complete interchange-JPEG file.
   * Pass TRUE unless you are very sure of what you're doing.
   */
  jpeg_start_compress(&cinfo, TRUE);

  /* Step 5: while (scan lines remain to be written) */
  /*           jpeg_write_scanlines(...); */

  /* Here we use the library's state variable cinfo.next_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   * To keep things simple, we pass one scanline per call; you can pass
   * more if you wish, though.
   */
  row_stride = image_width * 3;	/* JSAMPLEs per row in image_buffer */

  while (cinfo.next_scanline < cinfo.image_height) {
    /* jpeg_write_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could pass
     * more than one scanline at a time if that's more convenient.
     */
    row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
    (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
  }

  /* Step 6: Finish compression */

  jpeg_finish_compress(&cinfo);
  /* After finish_compress, we can close the output file. */
  fclose(outfile);

  /* Step 7: release JPEG compression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_compress(&cinfo);

  /* And we're done! */
  return 1;
}

gif显示

image-20240514113245283

标签:Jpeg,buffer,压缩,JPEG,width,算法,cinfo,jpeg,image
From: https://www.cnblogs.com/san39/p/18211684

相关文章

  • 数据分享|R语言逻辑回归、Naive Bayes贝叶斯、决策树、随机森林算法预测心脏病|附代码
    全文链接:http://tecdat.cn/?p=23061最近我们被客户要求撰写关于预测心脏病的研究报告,包括一些图形和统计输出。这个数据集可以追溯到1988年,由四个数据库组成。克利夫兰、匈牙利、瑞士和长滩。"目标"字段是指病人是否有心脏病。它的数值为整数,0=无病,1=有病数据集信息:目标:主......
  • 打印9*9乘法表(递归或压缩矩阵)python
    打印9*9表defprint_multiplication_table(row,col):ifrow>10:return#递归结束条件ifcol==row:print()#换行print_multiplication_table(row+1,1)#递归调用下一行else:print(f"{row-1}*{col}={(......
  • 【字符串常用算法】——KMP算法(你别闲烦 超详细,给你解释明白)
    1、字符串匹配——KMP算法    当我们想要想要在一个字符串中找到一个子串,如在字符串"aaabaaacaaad"中查找是否存在模式串"aaad"。首先常规的算法如下:1、先比较字符串与模式串 第一个是否相等,相等则匹配下一个2、比较第二个字符是否相等,相等则匹配下一个3、比......
  • 不闭合三维TSP:蜣螂优化算法DBO求解不闭合三维TSP(起点固定,终点不定,可以更改数据集),MATLA
    一、旅行商问题旅行商问题(Travelingsalesmanproblem,TSP)是一个经典的组合优化问题,它可以描述为一个商品推销员去若干城市推销商品,要求遍历所有城市后回到出发地,目的是选择一个最短的路线。当城市数目较少时,可以使用穷举法求解。而随着城市数增多,求解空间比较复杂,无法使......
  • 小白必看!AI产品经理的机器学习算法入门指南
    之前我们聊过关于人工智能的行业、产品经理的第二曲线以及两个岗位的区别,那这次我们再深入一层——趣解机器学习算法。机器学习算法可能听起来有些高深莫测,我明白很多人包括我一开始都感到头疼,我尽量不用公式,只用案例的形式来呈现,我们从整体到局部逐步深入。01机器学习算......
  • 算法随想录打卡第三天|链表
    //203移除链表元素publicListNoderemoveElements(ListNodehead,intval){//创建虚拟头指针不对//ListNoderes=head;//创建一个虚拟头结点ListNoderes=newListNode(val-1);res.next=head;ListNodeprev=res;......
  • Windows下分卷压缩后到Linux进行解压的方法
    windows分卷压缩后linux解压缩 Linux服务器在内网,中途隔了一层堡垒机。文件太大,堡垒机对此有限制,需要在Windows上分包,然后上送到Linux上进行合并解压。我探索出来的办法有两个,以下依次介绍:1、WinRAR+7za命令WinRAR上需要选择压缩格式为zip,输入分卷大小: 然后将分卷都传......
  • 【Algorithm算法章】递归&&搜索&&回溯&&算法思路总结概括
    文章目录......
  • 图论-二分图匹配匈牙利算法
    不得不说,如果以现实角度代入此算法的理解,就合理了很多,虽然有悖道德准则重点在于以下几点每次给女生分配男生前,都把男生全部初始化为可预定状态(即使他已经被别人成功匹配了)在所有女生中意的男生中遍历,如果发现该男生可预订就先预定,然后看他是否已经有主了,如果有主了,就dfs(matc......
  • 代码随想录算法训练营第一天 | 704.二分查找;27. 移除元素
    代码随想录算法训练营第一天|704.二分查找(红蓝模板法);27.移除元素(双指针法)704题链接:https://leetcode.cn/problems/binary-search/description/二分查找:https://programmercarl.com/0704.二分查找.html#其他语言版本二分查找红蓝法笔记:二分查找为什么总是写错?_哔哩哔哩_bil......