渐进式jpg转换成基线式 jpg
最近在搞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_Hjpeg_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; } #endif3、代碼編譯運行結果
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的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 洗大师权限管理分析
- 下一篇: 飞信的 SIP 协议分析