最近在搞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.html
https://www.xuanfengge.com/jpeg-format-baseline-and-progressive.html