渐进式jpg转换成基线式 jpg

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

1、两种格式的区别

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

e38c96d2-fe0a-11e4-9fc9-5a116df51920

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

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 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值