读取JPEG文件的压缩质量/质量因子参数

JPEG是一种图像压缩算法,而JPEG在做压缩编码的时候往往需要设定一个压缩质量/质量因子的参数(quality)。而在编码完成以后我们有时候希望获取到JPEG压缩文件的这个quality,好在万恶的jpeg编码协议却并没有直接存储这个quality参数,而opencv libjpeg这样的第三方库也没有提供获取JPEG文件质量参数的接口。可是JPEG文件存储了一个量化表的参数(quantize table),这里我们就是运用读取这个JPEG文件的quantize table然后反推这个JPEG文件的quality。

  1. 关于JPEG具体的压缩算法请查看wiki
  2. 需要用到第三方库文件:libjpeg(下载链接)
  3. 安装方法:
    tar xvf jpegsrc.v9.tar.gz
    cd jpeg-9
    mkdir build
    cd build
    ../configure
    make -j
    make install
    
  4. 实现代码

    #include <stdio.h>
    #include <math.h>
    
    #include "jpeglib.h"
    #include <setjmp.h>
    
    //标准明度量化表
    static const unsigned int std_luminance_quant_tbl[DCTSIZE2] = {
    16,  11,  10,  16,  24,  40,  51,  61,
    12,  12,  14,  19,  26,  58,  60,  55,
    14,  13,  16,  24,  40,  57,  69,  56,
    14,  17,  22,  29,  51,  87,  80,  62,
    18,  22,  37,  56,  68, 109, 103,  77,
    24,  35,  55,  64,  81, 104, 113,  92,
    49,  64,  78,  87, 103, 121, 120, 101,
    72,  92,  95,  98, 112, 100, 103,  99
    };
    //读取JPG文件的质量参数
    int ReadJpegQuality(const char *filename)
    {
      FILE * infile = fopen(filename, "rb");
      fseek(infile,0,SEEK_END);
      size_t sz = ftell(infile);
      fseek(infile,0,SEEK_SET);
      unsigned char* buffer = new unsigned char[sz];
      fread(buffer,1,sz,infile);
      fclose(infile);
      //如果不是JPG格式的文件返回-1
      if(buffer==NULL || sz <= 2 || 
      0xFF != (uint8_t)buffer[0] || 
      0xD8 != (uint8_t)buffer[1])
      {
        return -1;
      }
      struct jpeg_decompress_struct cinfo;
      struct jpeg_error_mgr jerr;
      cinfo.err = jpeg_std_error(&jerr);
      jpeg_create_decompress(&cinfo);
      jpeg_mem_src(&cinfo,(unsigned char*)buffer,sz);
      jpeg_read_header(&cinfo, TRUE);
      int tmp_quality = 0;
      int linear_quality = 0;
      const int aver_times = 3;
      int times = 0;
      int aver_quality = 0;
      //量化表反推3次,取平均值
      for(int i=0;i<DCTSIZE2;i++)
      {
        long temp = cinfo.quant_tbl_ptrs[0]->quantval[i];
        if(temp<32767L&&temp>0)
        {
          linear_quality = ceil((float)(temp*100L - 50L)/std_luminance_quant_tbl[i]);
          if(linear_quality==1) tmp_quality = 1;
          else if(linear_quality==100) tmp_quality = 100;
          else if(linear_quality>100)
          {
            tmp_quality = ceil((float)5000/linear_quality);
          }
        else
        {
          tmp_quality = 100 - ceil((float)linear_quality/2);
        }
        aver_quality += tmp_quality;
        if(aver_times==++times)
        {
          aver_quality /= aver_times;
          break;
        } 
        }
      }
      jpeg_destroy_decompress(&cinfo);
      return aver_quality;
    }
    
    int main(int argc,char** argv)
    {
      printf("quality: %d\n",ReadJpegQuality("test1.jpg"));
      return 0; 
    }
    
  5. 编译链接的方法也很简单了
    g++ main.cpp -I/your_path_to/libjpeg-9b/build/include -L/your_path_to/libjpeg-9b/build/lib/ -ljpeg
以下是一个简单的基于libjpeg库的JPEG数据压缩/解压算法代码示例: 压缩代码: ```c #include <stdio.h> #include <stdlib.h> #include <jpeglib.h> #define INPUT_FILE "input.jpg" #define OUTPUT_FILE "output.jpg" void compress_jpeg(const char* input_file, const char* output_file, int quality) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; FILE* infile = fopen(input_file, "rb"); if (infile == NULL) { fprintf(stderr, "Error opening input file.\n"); exit(EXIT_FAILURE); } FILE* outfile = fopen(output_file, "wb"); if (outfile == NULL) { fprintf(stderr, "Error opening output file.\n"); exit(EXIT_FAILURE); } cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); jpeg_stdio_dest(&cinfo, outfile); // 设置压缩参数 cinfo.image_width = 0; cinfo.image_height = 0; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality, TRUE); // 开始压缩 jpeg_start_compress(&cinfo, TRUE); // 读取图像数据并压缩 JSAMPROW row_pointer[1]; int row_stride; while (cinfo.next_scanline < cinfo.image_height) { row_pointer[0] = &buffer[cinfo.next_scanline * row_stride]; (void)jpeg_write_scanlines(&cinfo, row_pointer, 1); } // 完成压缩 jpeg_finish_compress(&cinfo); fclose(infile); fclose(outfile); jpeg_destroy_compress(&cinfo); } ``` 解压代码: ```c #include <stdio.h> #include <stdlib.h> #include <jpeglib.h> #define INPUT_FILE "input.jpg" #define OUTPUT_FILE "output.jpg" void decompress_jpeg(const char* input_file, const char* output_file) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; FILE* infile = fopen(input_file, "rb"); if (infile == NULL) { fprintf(stderr, "Error opening input file.\n"); exit(EXIT_FAILURE); } FILE* outfile = fopen(output_file, "wb"); if (outfile == NULL) { fprintf(stderr, "Error opening output file.\n"); exit(EXIT_FAILURE); } cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, infile); // 读取JPEG文件头信息 (void)jpeg_read_header(&cinfo, TRUE); // 开始解压 (void)jpeg_start_decompress(&cinfo); // 读取图像数据并解压 JSAMPARRAY buffer; int row_stride; buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, cinfo.image_width * cinfo.num_components, 1); while (cinfo.output_scanline < cinfo.output_height) { (void)jpeg_read_scanlines(&cinfo, buffer, 1); fwrite(buffer[0], cinfo.image_width * cinfo.num_components, 1, outfile); } // 完成解压 (void)jpeg_finish_decompress(&cinfo); fclose(infile); fclose(outfile); jpeg_destroy_decompress(&cinfo); } ``` 需要注意的是,上述代码仅提供了基本的JPEG数据压缩/解压功能,对于高质量JPEG压缩/解压需要使用更加复杂的算法和技术。同时,由于JPEG压缩是有损压缩,解压后的图像质量可能会有所下降,需要根据实际需求进行调整。
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值