最近在搞SSD202D平台的jpeg图片显示,发现progressive jpeg无法显示,于是就用libjpeg库progressive jpeg转换成baseline jpg

1、两种格式的区别

PEG有两种存储格式:baseline 和 progressive。Baseline JPEG 会在数据可用时,一行一行自上而下显示。Progressive JPEG会先显示模糊图片,然后逐渐清晰。

两种格式有相同尺寸以及图像数据,他们的扩展名也是相同的,唯一的区别是二者显示的方式不同。

Baseline JPEG

这种类型的JPEG文件存储方式是按从上到下的扫描方式,把每一行顺序的保存在JPEG文件中。打开这个文件显示它的内容时,数据将按照存储时的顺序从上到下一行一行的被显示出来,直到所有的数据都被读完,就完成了整张图片的显示。如果文件较大或者网络下载速度较慢,那么就会看到图片被一行行加载的效果,这种格式的JPEG没有什么优点,因此,一般都推荐使用Progressive JPEG

Progressive JPEG

和Baseline一遍扫描不同,Progressive JPEG文件包含多次扫描,这些扫描顺寻的存储在JPEG文件中。打开文件过程中,会先显示整个图片的模糊轮廓,随着扫描次数的增加,图片变得越来越清晰。这种格式的主要优点是在网络较慢的情况下,可以看到图片的轮廓知道正在加载的图片大概是什么。在一些网站打开较大图片时,你就会注意到这种技术。

那么如何利用libjpeg库来转换呢?

可通过分析JPEG文件格式,查找文件有SOF0 marker(baseline)还是SOF2 marker(progressive)

2、相关代码

这里使用的libjpeg_v9源码编译的库,相关代码已上传:

jpeg_v9_test_code.tar.gz-Linux文档类资源-CSDN下载

jpeg_to_baseline_jpeg.h

#ifndef _JPEG_TO_BASELINE_JPEG_H
#define _JPEG_TO_BASELINE_JPEG_H#ifdef __cplusplus
extern "C"
{
#endif#include <stdio.h>/*
1、检测图片文件是否为jpeg格式
2、如果图片是baseline格式的jpeg,不处理
3、如果不是baseline格式的jpeg,将其转换成baseline格式的jpeg
*/
int jpeg_to_baseline_jpeg(const char *file_path);#ifdef __cplusplus
}
#endif#endif // !_JPEG_TO_BASELINE_JPEG_H

jpeg_to_baseline_jpeg.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jpeglib.h"
#include <setjmp.h>
#include "jpeg_to_baseline_jpeg.h"#define THIS_FILE "jpeg_to_baseline_jpeg.c"typedef struct jpeg_error_mgr_usr
{struct jpeg_error_mgr pub;jmp_buf setjmp_buffer;
}jpeg_error_mgr_usr_t;static char jpeg_last_error_buf[256] = {0};static void my_error_exit(j_common_ptr cinfo)
{printf("[%s::%s:%d] my_error_exit\n", THIS_FILE, __FUNCTION__, __LINE__);if (cinfo != NULL){jpeg_error_mgr_usr_t *error_ptr = (jpeg_error_mgr_usr_t *)cinfo->err;if (cinfo->err != NULL  && cinfo->err->format_message != NULL){memset(jpeg_last_error_buf, 0, sizeof(jpeg_last_error_buf)/sizeof(jpeg_last_error_buf[0]));(*(cinfo->err->format_message)) (cinfo, jpeg_last_error_buf);}if (error_ptr != NULL){longjmp(error_ptr->setjmp_buffer, 1);}}
}// 根据文件头判断是否是jpeg文件
static int is_jpeg_file(const char *file_path)
{int ret = -1;if (file_path == NULL || file_path[0] == '\0'){return ret;}unsigned short BMP = 0x4D42, JPEG = 0xD8FF, PNG[4] = { 0x5089, 0x474E };FILE *fp = NULL;short int i = 0;unsigned short pis[5] = { 0 };fp = fopen(file_path, "r");if (fp == NULL){printf("[%s::%s:%d] can not read %s\n", THIS_FILE, __FUNCTION__, __LINE__, file_path);return ret;}else{fread((void *)pis, 8, 1, fp);fclose(fp);}if (pis[0] == JPEG){printf("[%s::%s:%d] %s is JPEG file\n", THIS_FILE, __FUNCTION__, __LINE__, file_path);ret = 0;}return ret;
}int jpeg_to_baseline_jpeg(const char *file_path)
{int ret = -1;if (is_jpeg_file(file_path) != 0){return ret;}struct jpeg_decompress_struct out_cinfo = {0};struct jpeg_error_mgr_usr jpeg_err = {0};FILE * infile = NULL;JSAMPARRAY buffer;unsigned char *tmp = NULL;int row_stride = 0;unsigned char *output_buffer = NULL;unsigned int out_image_width = 0;unsigned int out_image_height = 0;int out_image_components = 0;J_COLOR_SPACE image_in_color_space = JCS_UNKNOWN;//打开指定图像文件infile = fopen(file_path, "rb");if (infile == NULL){printf("[%s::%s:%d] file %s open failed!!!\n", THIS_FILE, __FUNCTION__, __LINE__, file_path);return ret;}//绑定标准错误处理结构out_cinfo.err = jpeg_std_error(&jpeg_err.pub);jpeg_err.pub.error_exit = my_error_exit; // 自定义错误处理函数if (setjmp(jpeg_err.setjmp_buffer)){/*在正常情况下,setjmp将返回0,而如果程序出现错误,即调用my_error_exit然后程序将再次跳转于此,同时setjmp将返回在my_error_exit中由longjmp第二个参数设定的值1并执行以下代码*/printf("[%s::%s:%d] jpegLastErrorMsg:%s\n", THIS_FILE, __FUNCTION__, __LINE__, jpeg_last_error_buf);//out_cinfo.err->msg_code;jpeg_destroy_decompress(&out_cinfo);if (infile){fclose(infile);infile = NULL;}return ret;}//初始化JPEG解码对象jpeg_create_decompress(&out_cinfo);// 指定数据源jpeg_stdio_src(&out_cinfo, infile);//读取图像信息,即jpeg图片文件参数(void)jpeg_read_header(&out_cinfo, TRUE);// 如果原文件就是baseline格式的,就不需要转换if (out_cinfo.is_baseline == 1) // Baseline SOF0 encountered {if (infile){fclose(infile);infile = NULL;}jpeg_destroy_decompress(&out_cinfo);ret = 0;printf("[%s::%s:%d] picture %s baseline jpeg\n", THIS_FILE, __FUNCTION__, __LINE__, file_path);return ret;}printf("[%s::%s:%d] picture %s progressive jpeg\n", THIS_FILE, __FUNCTION__, __LINE__, file_path);//开始解压缩图像(void)jpeg_start_decompress(&out_cinfo);out_image_width = out_cinfo.output_width;out_image_height = out_cinfo.output_height;out_image_components = out_cinfo.output_components;image_in_color_space = out_cinfo.out_color_space;printf("[%s::%s:%d] out_image_components:%d, image_in_color_space:%d\n", THIS_FILE, __FUNCTION__, __LINE__, out_image_components, image_in_color_space);//分配缓冲区空间row_stride = out_cinfo.output_width * out_cinfo.output_components; // 每一行的步长buffer = (*out_cinfo.mem->alloc_sarray)((j_common_ptr)&out_cinfo, JPOOL_IMAGE, row_stride, 1);if (buffer == NULL){jpeg_destroy_decompress(&out_cinfo);if (infile != NULL){fclose(infile);infile = NULL;}ret = -1;return ret;}output_buffer = (unsigned char *)malloc(row_stride * out_cinfo.output_height);if (output_buffer == NULL){jpeg_destroy_decompress(&out_cinfo);if (infile != NULL){fclose(infile);infile = NULL;}ret = -1;return ret;}memset(output_buffer, 0, row_stride * out_cinfo.output_height);tmp = output_buffer;//读取数据while (out_cinfo.output_scanline < out_cinfo.output_height)  //scanline表示当前已读取行数,此循环依次读取图片所有数据{(void)jpeg_read_scanlines(&out_cinfo, buffer, 1);//将数据一行一行读取memcpy(tmp, *buffer, row_stride);tmp += row_stride;}//结束解压缩操作(void)jpeg_finish_decompress(&out_cinfo);//释放资源jpeg_destroy_decompress(&out_cinfo);// 关闭文件if (infile != NULL){fclose(infile);infile = NULL;}//变量定义struct jpeg_compress_struct input_cinfo;struct jpeg_error_mgr_usr input_jerr;FILE *outfile = NULL;JSAMPROW row_pointer[1];//指定目标输出图像文件outfile = fopen(file_path, "wb");if (outfile == NULL){if (output_buffer != NULL){free(output_buffer);output_buffer = NULL;}ret = -1;return ret;}//绑定标准错误处理结构input_cinfo.err = jpeg_std_error(&input_jerr.pub);input_jerr.pub.error_exit = my_error_exit;if (setjmp(input_jerr.setjmp_buffer)){/*在正常情况下,setjmp将返回0,而如果程序出现错误,即调用my_error_exit然后程序将再次跳转于此,同时setjmp将返回在my_error_exit中由longjmp第二个参数设定的值1并执行以下代码*/printf("[%s::%d] jpegLastErrorMsg : %s\n", __FUNCTION__, __LINE__, jpeg_last_error_buf);jpeg_destroy_compress(&input_cinfo);if (outfile != NULL){fclose(outfile);outfile = NULL;}if (output_buffer != NULL){free(output_buffer);output_buffer = NULL;}ret = -1;return ret;}//初始化JPEG对象jpeg_create_compress(&input_cinfo);// 指定输出对象jpeg_stdio_dest((struct jpeg_compress_struct *)&input_cinfo, outfile);//设定压缩参数input_cinfo.image_width = out_image_width;input_cinfo.image_height = out_image_height;input_cinfo.input_components = out_image_components;input_cinfo.in_color_space = image_in_color_space;jpeg_set_defaults((struct jpeg_compress_struct *)&input_cinfo);//此处设压缩比为70%,强制使用baseline格式jpeg_set_quality((struct jpeg_compress_struct *)&input_cinfo, 70, TRUE);//开始压缩jpeg_start_compress((struct jpeg_compress_struct *)&input_cinfo, TRUE);//写入数据while (input_cinfo.next_scanline < input_cinfo.image_height){row_pointer[0] = &output_buffer[input_cinfo.next_scanline*out_image_components*out_image_width];(void)jpeg_write_scanlines((struct jpeg_compress_struct *)&input_cinfo, (JSAMPROW *)&row_pointer, 1);}//压缩完毕jpeg_finish_compress((struct jpeg_compress_struct *)&input_cinfo);//释放资源if (outfile != NULL){fclose(outfile);outfile = NULL;}jpeg_destroy_compress((struct jpeg_compress_struct *)&input_cinfo);if (output_buffer != NULL){free(output_buffer);output_buffer = NULL;}return 0;
}/*
编译
arm-linux-gnueabihf-gcc jpeg_to_baseline_jpeg.c -o jpeg_tranfsfer -L. -ljpeg -Wl,-rpath=. -DTEST_ENA=1
*/
#if defined(TEST_ENA) && (TEST_ENA != 0)
int main(int argc, char* argv[])
{if (argc != 2){printf("args should be tow, such as: ./proc 1.jpg\n");return 0;}jpeg_to_baseline_jpeg(argv[1]);return 0;
}
#endif

3、代码编译运行结果

arm-linux-gnueabihf-gcc jpeg_to_baseline_jpeg.c -o jpeg_tranfsfer -L. -ljpeg -Wl,-rpath=. -DTEST_ENA=1

参考文献:

Baseline JPEG和Progressive JPEG的区别https://blog.csdn.net/kickxxx/article/details/8109356https://blog.csdn.net/kickxxx/article/details/8109356关于JPEG存储格式:baseline与progressivehttps://www.xuanfengge.com/jpeg-format-baseline-and-progressive.htmlhttps://www.xuanfengge.com/jpeg-format-baseline-and-progressive.html

渐进式jpg转换成基线式 jpg相关推荐

  1. php 下划线转大写开头,使用PHP把下划线分隔命名的字符串 转换成驼峰式命名方式 , 把下划线后面的第一个字母变成大写...

    最近项目使用symfony框架,这个框架对数据库的操作在这个团队里使用的是ORM进行操作,说实话使用ORM的开发效率和运行效率不一定高多少,到是它的实体命名和现有数据库字段的命名不太一样,ORM实体属 ...

  2. SDUT-2132_数据结构实验之栈与队列二:一般算术表达式转换成后缀式

    数据结构实验之栈与队列二:一般算术表达式转换成后缀式 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 对于一个基于二元运 ...

  3. 一般算术表达式转换成后缀式

    数据结构实验之栈与队列二:一般算术表达式转换成后缀式 对于一个基于二元运算符的算术表达式,转换为对应的后缀式,并输出之. Input 输入一个算术表达式,以'#'字符作为结束标志. Output 输出 ...

  4. 二叉树:表达式二叉树转换成中缀式(括弧处理)

    文章目录 问题描述 : 输入说明 : 输出说明 : 输入范例 : 输出范例 : 思路分析 代码实现 事故现场 第一次提交 分析与总结 如果不妥请留言,你的关注和回复是对我最大的鼓励,谢谢!如果想立即回 ...

  5. 要求实现编译器的以下功能: (1) 按规则拼单词,并转换成二元式形式 (2) 删除注释行 (3) 删除空白符 (空格、回车符、制表符) (4) 列表打印源程序,按照源程序的行打印,在每行的前面加上行号

    目录 目录    2 1 实验目的··· 3 2 实验内容··· 3 2.1 TINY计算机语言描述··· 3 2.2 实验要求··· 3 3 此法分析器的程序实现··· 4 3.1 状态转换图··· ...

  6. 数据结构实验之栈与队列二:一般算术表达式转换成后缀式

    Description 对于一个基于二元运算符的算术表达式,转换为对应的后缀式,并输出之. Input 输入一个算术表达式,以'#'字符作为结束标志. Output 输出该表达式转换所得到的后缀式. ...

  7. 数据结构实验之栈二:一般算术表达式转换成后缀式

    题目描述 对于一个基于二元运算符的算术表达式,转换为对应的后缀式,并输出之. 输入 输入一个算术表达式,以'#'字符作为结束标志. 输出 输出该表达式转换所得到的后缀式. 示例输入 a*b+(c-d/ ...

  8. B - 数据结构实验之栈与队列二:一般算术表达式转换成后缀式

    理解:分成4种情况这里不介绍,这里讲本质.后缀表达式的运算符优先级,是把前缀的运算符排列出一个优先级,然后从最优先输出的,不一定连续输出.而4种情况就是对优先级的处理. Description 对于一 ...

  9. SDUT OJ 2132 (一般算术表达式转换成后缀式)

    题目描述 Description 对于一个基于二元运算符的算术表达式,转换为对应的后缀式,并输出之. Input 输入一个算术表达式,以'#'字符作为结束标志. Output 输出该表达式转换所得到的 ...

  10. PHP把下划线分隔命名的字符串 转换成驼峰式命名方式

    <?php //微秒时间 function microtime_float() {list($usec, $sec) = explode(" ", microtime()); ...

最新文章

  1. Python 的 eval() 与 exec()区别
  2. python网络安全工具箱界面_Python开发案例:设计启动工具箱,显示图形界面的方式...
  3. 鸿蒙霸榜 GitHub,从最初的 Plan B 到“取代 Android”?
  4. sts 明明导包正确却报错_这真是危险的关系,明明你错了,但是她认了!
  5. ASP.NET Core Docker Nginx分权,多网站部署
  6. JDWP Transport dt socket failed to initialize
  7. JAVA bridge设计模式,java设计模式之Bridge
  8. Android模拟器PANIC: Could not open:问题解决方法
  9. eclips常用快捷键
  10. 2018-05-16树莓派如何开启UART串口
  11. oracle .net 中文,asp.net查出 oracle数据库中的中文乱码问题
  12. 关于鼓励软件产业和集成电路产业发展有关税收政策问题的通知
  13. paip.2013年技术趋势以及热点 v3.0 cao
  14. SBUS协议:SBUS解析与合成
  15. 全网最新猎豹网校-快速掌握Python项目实战
  16. java栅栏_Java并发工具类(栅栏CyclicBarrier)
  17. 2017年10月19日 第十次总结
  18. DRB-GAN: A Dynamic ResBlock Generative Adversarial Network for Artistic Style Transfer
  19. [置顶]生鲜配送管理系统_升鲜宝V2.0 销售订单汇总_采购任务分配功能_操作说明...
  20. 7-172 元宵花灯

热门文章

  1. poj3580:SuperMemo(块状链表/Splay)
  2. 短视频开发app,Android 强制应用全局横屏或竖屏
  3. LaTeX 排版(二)——排版数学公式
  4. 对于超前,滞后,超前滞后使用范围
  5. 如何更改文件夹图标和颜色
  6. 华为云上云迁移工具案例实践:阿里云迁移到华为云
  7. mysql数据库首次查询缓慢
  8. 博途1200/1500PLC斜坡指令RAMP(带暂停功能)
  9. IT行业主要职业有什么?
  10. 2019/04/02 实现互联网的DNS架构