今天研究在SSD202D平台使用libjpeg和lodepng将jpg图片转换成rgb888格式的png图片,分享一下研究的结果:
1、准备工作
下载libjpeg:libjpeg
这里使用的 jpegsrc.v9.tar.gz
下载lodepng:lodepng
2、相关代码
jpeg_transfer.h
#ifndef _JPEG_TRANSFER_H
#define _JPEG_TRANSFER_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);
// jpg转换成rgb24格式的png
int jpeg_to_rgb24_png(const char *jpg_file_path, const char * png_file_path);
#ifdef __cplusplus
}
#endif
#endif // !_JPEG_TRANSFER_H
jpeg_transfer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "jpeglib.h"
#include <setjmp.h>
#include "lodepng.h"
#include "jpeg_transfer.h"
#define THIS_FILE "jpeg_transfer.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;
}
static int is_png_file(const char * file_path)
{
int ret = -1;
if (file_path != NULL && file_path[0] != '\0')
{
FILE * file = NULL;
unsigned short header[5] = { 0 };
unsigned short png_header[4] = { 0x5089, 0x474E, 0x0A0D, 0x0A1A };
file = fopen(file_path, "r");
if (file != NULL)
{
fread(header, 8, 1, file);
fclose(file);
if (png_header[0]==header[0] &&
png_header[1]==header[1] &&
png_header[2]==header[2] &&
png_header[3]==header[3])
{
ret = 0;
}
}
else
{
printf("[%s:%d] file %s open failed\n", __FUNCTION__, __LINE__, file_path);
ret = -1;
}
}
return ret;
}
typedef struct _image_data
{
unsigned char *pixels;
unsigned int img_width;
unsigned int img_height;
unsigned int img_color_components;
unsigned int img_color_space;
}image_data_t;
int jpeg_to_rgb24_png(const char *jpg_file_path, const char * png_file_path)
{
int ret = -1;
struct jpeg_decompress_struct out_cinfo = {0};
struct jpeg_error_mgr_usr jpeg_err = {0};
FILE * infile = NULL;
JSAMPARRAY buffer;
unsigned int row_stride = 0;
image_data_t img_data = { 0 };
if (lb_is_jpeg_file(jpg_file_path) != 0)
{
return ret;
}
//打开指定图像文件
infile = fopen(jpg_file_path, "rb");
if (infile == NULL)
{
printf("[%s::%s:%d] file %s open failed!!!\n", THIS_FILE, __FUNCTION__, __LINE__, jpg_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);
if (out_cinfo.is_baseline == 1) // Baseline SOF0 encountered
{
printf("[%s::%s:%d] picture %s baseline jpeg\n", THIS_FILE, __FUNCTION__, __LINE__, jpg_file_path);
}
else
{
printf("[%s::%s:%d] picture %s progressive jpeg\n", THIS_FILE, __FUNCTION__, __LINE__, jpg_file_path);
}
//开始解压缩图像
(void)jpeg_start_decompress(&out_cinfo);
img_data.img_width = out_cinfo.output_width;
img_data.img_height = out_cinfo.output_height;
img_data.img_color_components = out_cinfo.output_components;
img_data.img_color_space = out_cinfo.out_color_space;
row_stride = img_data.img_width * img_data.img_color_components; // 每一行的步长
printf("[%s::%s:%d] width:%d, height:%d, components:%d, color_space:%d\n", THIS_FILE, __FUNCTION__, __LINE__, img_data.img_width, img_data.img_height, img_data.img_color_components, img_data.img_color_space);
//分配缓冲区空间
if ( (*out_cinfo.mem->alloc_sarray) == NULL)
{
printf("[%s::%s:%d] alloc_sarray is NULL\n", THIS_FILE, __FUNCTION__, __LINE__);
jpeg_destroy_decompress(&out_cinfo);
if (infile != NULL)
{
fclose(infile);
infile = NULL;
}
ret = -1;
return ret;
}
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;
}
img_data.pixels = (unsigned char *)malloc(row_stride * img_data.img_height);
if (img_data.pixels == NULL)
{
jpeg_destroy_decompress(&out_cinfo);
if (infile != NULL)
{
fclose(infile);
infile = NULL;
}
ret = -1;
return ret;
}
memset(img_data.pixels, 0, row_stride * out_cinfo.output_height);
unsigned char * ptr = img_data.pixels;
while (out_cinfo.output_scanline < out_cinfo.output_height) //读取数据 scanline表示当前已读取行数,此循环依次读取图片所有数据
{
(void)jpeg_read_scanlines(&out_cinfo, buffer, 1);//将数据一行一行读取
memcpy(ptr, *buffer, row_stride);
ptr += row_stride;
}
//结束解压缩操作
(void)jpeg_finish_decompress(&out_cinfo);
//释放资源
jpeg_destroy_decompress(&out_cinfo);
// 关闭文件
if (infile != NULL)
{
fclose(infile);
infile = NULL;
}
if (img_data.img_color_components == 3) // 位深度:3x8=24 RGB888
{
unsigned int error = lodepng_encode24_file(png_file_path, img_data.pixels, img_data.img_width, img_data.img_height);
if (error != 0)
{
printf("[%s::%s:%d] error %u: %s\n", THIS_FILE, __FUNCTION__, __LINE__, error, lodepng_error_text(error));
ret = -1;
}
else
{
ret = 0;
}
}
else if (img_data.img_color_components == 4) // 位深度:4x8=32 ARGB8888
{
//TODO::
printf("[%s::%s:%d] img_color_components:%u\n", THIS_FILE, __FUNCTION__, __LINE__, img_data.img_color_components);
}
if (img_data.pixels != NULL)
{
free(img_data.pixels);
img_data.pixels = NULL;
}
return ret;
}
/*
编译
arm-linux-gnueabihf-gcc jpeg_transfer.c lodepng.c -o jpeg_transfer -L. -ljpeg -Wl,-rpath=. -DTEST_ENA=1
*/
#if defined(TEST_ENA) && (TEST_ENA != 0)
int main(int argc, char* argv[])
{
if (argc != 3)
{
printf("args should be tow, such as: ./program_name 1.jpg 2.png\n");
return 0;
}
jpeg_to_rgb24_png(argv[1], argv[2]);
return 0;
}
#endif
3、编译和运行效果
arm-linux-gnueabihf-gcc jpeg_transfer.c lodepng.c -o jpeg_transfer -L. -ljpeg -Wl,-rpath=. -DTEST_ENA=1
相关代码:code