图像编码算法都有相应的质量参数,如hevc编码中的qp值(值越大,压缩率越高),jpeg中的quality(对应到DCT变换后的量化程度)。最近看了看如何根据jpeg图像中的量化文件统计其quality参数,记录下过程。
jpeg文件中可以携带icc颜色配置信息,这个参数告诉显示器用什么色域来显示图像,如下图文件中包含了Adobe RGB色彩描述文件,icc文件大小通常为几百个字节,嵌入在APP2 marker(0xe2)中。
我的环境:linux centos、libjpeg turbo库
Talk is cheap, Show me your code…
#include <stdio.h>
#include <math.h>
#include "jpeglib.h"
#include <setjmp.h>
#include "turbojpeg.h"
#include "cdjpeg.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
};
//标准色度分量量化表
static const unsigned int std_chrominance_quant_tbl[DCTSIZE2] = {
17, 18, 24, 47, 99, 99, 99, 99,
18, 21, 26, 66, 99, 99, 99, 99,
24, 26, 56, 99, 99, 99, 99, 99,
47, 66, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99
};
const char *subsampName[TJ_NUMSAMP] = {
"4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0", "4:1:1"
};
const char *colorspaceName[TJ_NUMCS] = {
"RGB", "YCbCr", "GRAY", "CMYK", "YCCK"
};
//读取JPG文件的质量参数
int ReadJpegInfo(const char *filename, char *icc_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 != (unsigned char)buffer[0] || 0xD8 != (unsigned char)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_save_markers(&cinfo, JPEG_APP0 + 2, 0xFFFF);
jpeg_read_header(&cinfo, TRUE);
int tmp_quality = 0;
int linear_quality = 0;
const int aver_times = 64;
int times = 0;
int aver_quality = 0;
long temp1 = 0;
long temp2 = 0;
//get jpeg file info
int src_width = cinfo.image_width;
int src_height = cinfo.image_height;
//int jpegSubsamp = getSubsamp(cinfo);
//int jpegHorSamp = cinfo.h_samp_factor;
//int jpegVerSamp = cinfo.v_samp_factor;
int jpegColorspace = 1; //default
switch (cinfo.jpeg_color_space) {
case JCS_GRAYSCALE: jpegColorspace = TJCS_GRAY; break;
case JCS_RGB: jpegColorspace = TJCS_RGB; break;
case JCS_YCbCr: jpegColorspace = TJCS_YCbCr; break;
case JCS_CMYK: jpegColorspace = TJCS_CMYK; break;
case JCS_YCCK: jpegColorspace = TJCS_YCCK; break;
default: jpegColorspace = -1; break;
}
printf("Input Image: %d x %d pixels, %s colorspace\n", src_width, src_height, colorspaceName[jpegColorspace]);
//量化表反推,取平均值
for(int i = 0; i < DCTSIZE2; i++)
{
temp1 = cinfo.quant_tbl_ptrs[0]->quantval[i];
temp2 = cinfo.quant_tbl_ptrs[1]->quantval[i];
printf("DCTQUA1[%d] = %ld, DCTQUA2[%d] = %ld\n", i, temp1, i, temp2);
if(temp1 < 32767L && temp1 > 0)
{
linear_quality = ceil((float)(temp1 * 100L - 50L)/std_luminance_quant_tbl[i]); //100L表示long int
if(linear_quality == 1)
tmp_quality = 1;
else if(linear_quality == 100)
tmp_quality = 50;
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;
times++;
if(aver_times == times)
{
aver_quality = aver_quality / aver_times;
break;
}
}
}
if (icc_filename != NULL)
{
FILE *icc_file;
JOCTET *icc_profile;
unsigned int icc_len;
if ((icc_file = fopen(icc_filename, "wb")) == NULL) {
printf("can't open %s\n", icc_filename);
}
if (jpeg_read_icc_profile(&cinfo, &icc_profile, &icc_len)) {
printf("icc_len = %d\n", icc_len);
if (fwrite(icc_profile, icc_len, 1, icc_file) < 1) {
printf("can't read ICC profile from %s\n", icc_filename);
free(icc_profile);
fclose(icc_file);
}
free(icc_profile);
fclose(icc_file);
}
else
{
printf("no icc file found\n");
}
//else if (cinfo.err->msg_code != JWRN_BOGUS_ICC)
// printf("no ICC profile data in JPEG file\n");
}
jpeg_destroy_decompress(&cinfo);
return aver_quality;
}
// g++ -L /opt/libjpeg-turbo/lib64 -lturbojpeg -ljpeg get_jpg_quality.cpp -o quality
// ./quality xxx.jpg
int main(int argc, char **argv)
{
if(argc != 2)
{
printf("parameter number should set to 2\n");
return -1;
}
char icc_filename[100] = {0};
sprintf(icc_filename, "%s.icc", argv[1]);
printf("quality: %d\n", ReadJpegInfo(argv[1], icc_filename));
return 0;
}
运行结果如下,计算得到quality为92.
参考
[1] libjpeg-turbo/libjpeg-turbo