天天看点

jpeg图像质量参数及icc信息提取

图像编码算法都有相应的质量参数,如hevc编码中的qp值(值越大,压缩率越高),jpeg中的quality(对应到DCT变换后的量化程度)。最近看了看如何根据jpeg图像中的量化文件统计其quality参数,记录下过程。

jpeg文件中可以携带icc颜色配置信息,这个参数告诉显示器用什么色域来显示图像,如下图文件中包含了Adobe RGB色彩描述文件,icc文件大小通常为几百个字节,嵌入在APP2 marker(0xe2)中。

jpeg图像质量参数及icc信息提取

我的环境: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.

jpeg图像质量参数及icc信息提取

参考

[1] libjpeg-turbo/libjpeg-turbo

继续阅读