一、JPEG简介
JPEG( Joint Photographic Experts Group)即联合图像专家组,是用于连续色调静态图像压缩的一种标准,文件后缀名为.jpg或.jpeg,是最常用的图像文件格式。
其主要是采用预测编码(DPCM)、离散余弦变换(DCT)以及熵编码的联合编码方式,以去除冗余的图像和彩色数据,属于有损压缩格式,它能够将图像压缩在很小的储存空间,一定程度上会造成图像数据的损伤。尤其是使用过高的压缩比例,将使最终解压缩后恢复的图像质量降低,如果追求高品质图像,则不宜采用过高的压缩比例。
JPEG的性能,用质量与比特率之比来衡量,是相当优越的。
二、实验原理
三、解码过程
(1)读取文件
(2)解析 Segment Marker
①解析 SOI
②解析 APP0
③解析 DQT
④解析 SOF0
⑤解析 DHT
⑥解析 SOS
得到解析每个颜色分量的 DC 、 AC 值所使用的 Huffman 表序号(与 DHT
中序号对应)
(3)依据每个分量的水平、垂直采样因子计算 MCU 的大小,并得到每个 MCU 中 8*8
宏块的个数
(4)对每个 MCU 解码(依照各分量水平、垂直采样因子对 MCU 中每个分量宏块解
码)
(5)解析到 EOI,解码结束
(6)将 Y、Cb、Cr 转化为需要的色彩空间并保存。
四、代码实现
(一)结构体
①huffman_table:
struct huffman_table
{
/* Fast look up table, using HUFFMAN_HASH_NBITS bits we can have directly the symbol,
* if the symbol is <0, then we need to look into the tree table */
short int lookup[HUFFMAN_HASH_SIZE];
/* code size: give the number of bits of a symbol is encoded */
unsigned char code_size[HUFFMAN_HASH_SIZE];
/* some place to store value that is not encoded in the lookup table
* FIXME: Calculate if 256 value is enough to store all values
*/
uint16_t slowtable[16-HUFFMAN_HASH_NBITS][256];
};
②component:
struct component
{
unsigned int Hfactor;
unsigned int Vfactor;
float *Q_table; /* Pointer to the quantisation table to use */
struct huffman_table *AC_table;
struct huffman_table *DC_table;
short int previous_DC; /* Previous DC coefficient */
short int DCT[64]; /* DCT coef */
#if SANITY_CHECK
unsigned int cid;
#endif
};
③jdec_private
struct jdec_private
{
/* Public variables */
uint8_t *components[COMPONENTS];
unsigned int width, height; /* Size of the image */
unsigned int flags;
/* Private variables */
const unsigned char *stream_begin, *stream_end;
unsigned int stream_length;
const unsigned char *stream; /* Pointer to the current stream */
unsigned int reservoir, nbits_in_reservoir;
struct component component_infos[COMPONENTS];
float Q_tables[COMPONENTS][64]; /* quantization tables */
struct huffman_table HTDC[HUFFMAN_TABLES]; /* DC huffman tables */
struct huffman_table HTAC[HUFFMAN_TABLES]; /* AC huffman tables */
int default_huffman_table_initialized;
int restart_interval;
int restarts_to_go; /* MCUs left in this restart interval */
int last_rst_marker_seen; /* Rst marker is incremented each time */
/* Temp space used after the IDCT to store each components */
uint8_t Y[64*4], Cr[64], Cb[64];
jmp_buf jump_state;
/* Internal Pointer use for colorspace conversion, do not modify it !!! */
uint8_t *plane[COMPONENTS];
};
(二)主函数
int main(int argc, char *argv[])
{
int output_format = TINYJPEG_FMT_YUV420P;
char *output_filename, *input_filename;
clock_t start_time, finish_time;
unsigned int duration;
int current_argument;
int benchmark_mode = 0;
#if TRACE //设定trace,边解码边写入文件
p_trace=fopen(TRACEFILE,"w");
if (p_trace==NULL)
{
printf("trace file open error!");
}
#endif
if (argc < 3)
usage();
current_argument = 1;
while (1)
{
if (strcmp(argv[current_argument], "--benchmark")==0) //strcmp字符串比较,用于比较两个字符串,如果二者相等返回0
benchmark_mode = 1; //如果相等,说明设置了benchmark,因此benchmark_mode置1
else
break;
current_argument++;
}
if (argc < current_argument+2)
usage();
input_filename = argv[current_argument]; //输入文件名,是argv[1]
if (strcmp(argv[current_argument+1],"yuv420p")==0) //argv[2]判断输出格式,这里选择yuv420p
output_format = TINYJPEG_FMT_YUV420P;
else if (strcmp(argv[current_argument+1],"rgb24")==0)
output_format = TINYJPEG_FMT_RGB24;
else if (strcmp(argv[current_argument+1],"bgr24")==0)
output_format = TINYJPEG_FMT_BGR24;
else if (strcmp(argv[current_argument+1],"grey")==0)
output_format = TINYJPEG_FMT_GREY;
else
exitmessage("Bad format: need to be one of yuv420p, rgb24, bgr24, grey\n");
output_filename = argv[current_argument+2]; //输出文件名,设为argv[3]
start_time = clock(); //开始
if (benchmark_mode) //是否多次解码,若设置了benchmark_mode就调用load_multiple_times,否则就调用convert_one_image
load_multiple_times(input_filename, output_filename, output_format);
else
convert_one_image(input_filename, output_filename, output_format); //核心函数
finish_time = clock(); //结束
duration = finish_time - start_time;
snprintf(error_string, sizeof(error_string),"Decoding finished in %u ticks\n", duration);
#if TRACE
fclose(p_trace);
#endif
return 0;
}
(三)DC图像的输出
int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)
{
unsigned int x, y, xstride_by_mcu, ystride_by_mcu;
unsigned int bytes_per_blocklines[3], bytes_per_mcu[3];
decode_MCU_fct decode_MCU;
const decode_MCU_fct *decode_mcu_table;
const convert_colorspace_fct *colorspace_array_conv;
convert_colorspace_fct convert_to_pixfmt;
if (setjmp(priv->jump_state))
return -1;
/* To keep gcc happy initialize some array */
bytes_per_mcu[1] = 0;
bytes_per_mcu[2] = 0;
bytes_per_blocklines[1] = 0;
bytes_per_blocklines[2] = 0;
decode_mcu_table = decode_mcu_3comp_table;
switch (pixfmt) {
case TINYJPEG_FMT_YUV420P:
colorspace_array_conv = convert_colorspace_yuv420p;
if (priv->components[0] == NULL)
priv->components[0] = (uint8_t *)malloc(priv->width * priv->height);
if (priv->components[1] == NULL)
priv->components[1] = (uint8_t *)malloc(priv->width * priv->height/4);
if (priv->components[2] == NULL)
priv->components[2] = (uint8_t *)malloc(priv->width * priv->height/4);
bytes_per_blocklines[0] = priv->width;
bytes_per_blocklines[1] = priv->width/4;
bytes_per_blocklines[2] = priv->width/4;
bytes_per_mcu[0] = 8;
bytes_per_mcu[1] = 4;
bytes_per_mcu[2] = 4;
break;
case TINYJPEG_FMT_RGB24:
colorspace_array_conv = convert_colorspace_rgb24;
if (priv->components[0] == NULL)
priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3);
bytes_per_blocklines[0] = priv->width * 3;
bytes_per_mcu[0] = 3*8;
break;
case TINYJPEG_FMT_BGR24:
colorspace_array_conv = convert_colorspace_bgr24;
if (priv->components[0] == NULL)
priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3);
bytes_per_blocklines[0] = priv->width * 3;
bytes_per_mcu[0] = 3*8;
break;
case TINYJPEG_FMT_GREY:
decode_mcu_table = decode_mcu_1comp_table;
colorspace_array_conv = convert_colorspace_grey;
if (priv->components[0] == NULL)
priv->components[0] = (uint8_t *)malloc(priv->width * priv->height);
bytes_per_blocklines[0] = priv->width;
bytes_per_mcu[0] = 8;
break;
default:
#if TRACE
fprintf(p_trace,"Bad pixel format\n");
fflush(p_trace);
#endif
return -1;
}
xstride_by_mcu = ystride_by_mcu = 8;
if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) {
decode_MCU = decode_mcu_table[0];
convert_to_pixfmt = colorspace_array_conv[0];
#if TRACE
fprintf(p_trace,"Use decode 1x1 sampling\n");
fflush(p_trace);
#endif
} else if (priv->component_infos[cY].Hfactor == 1) {
decode_MCU = decode_mcu_table[1];
convert_to_pixfmt = colorspace_array_conv[1];
ystride_by_mcu = 16;
#if TRACE
fprintf(p_trace,"Use decode 1x2 sampling (not supported)\n");
fflush(p_trace);
#endif
} else if (priv->component_infos[cY].Vfactor == 2) {
decode_MCU = decode_mcu_table[3];
convert_to_pixfmt = colorspace_array_conv[3];
xstride_by_mcu = 16;
ystride_by_mcu = 16;
#if TRACE
fprintf(p_trace,"Use decode 2x2 sampling\n");
fflush(p_trace);
#endif
} else {
decode_MCU = decode_mcu_table[2];
convert_to_pixfmt = colorspace_array_conv[2];
xstride_by_mcu = 16;
#if TRACE
fprintf(p_trace,"Use decode 2x1 sampling\n");
fflush(p_trace);
#endif
}
resync(priv);
/* Don't forget to that block can be either 8 or 16 lines */
bytes_per_blocklines[0] *= ystride_by_mcu;
bytes_per_blocklines[1] *= ystride_by_mcu;
bytes_per_blocklines[2] *= ystride_by_mcu;
bytes_per_mcu[0] *= xstride_by_mcu/8;
bytes_per_mcu[1] *= xstride_by_mcu/8;
bytes_per_mcu[2] *= xstride_by_mcu/8;
//添加定义
FILE* DCFile = fopen("DC.yuv", "wb");
FILE* ACFile = fopen("AC.yuv", "wb");
unsigned char* uv_buf = 128;
unsigned char* dc_buf, * ac_buf;
double dc_freq[256] = { 0 }, ac_freq[256] = { 0 };
/* Just the decode the image by macroblock (size is 8x8, 8x16, or 16x16) */
for (y=0; y < priv->height/ystride_by_mcu; y++)
{
//trace("Decoding row %d\n", y);
priv->plane[0] = priv->components[0] + (y * bytes_per_blocklines[0]);
priv->plane[1] = priv->components[1] + (y * bytes_per_blocklines[1]);
priv->plane[2] = priv->components[2] + (y * bytes_per_blocklines[2]);
for (x=0; x < priv->width; x+=xstride_by_mcu)
{
decode_MCU(priv);
使用的yuv文件如下:
//判断是否能打开yuv文件
if (DCFile == NULL)
{
printf("Fail to open DC\n");
return 0;
}
if (ACFile == NULL)
{
printf("Fail to open AC\n");
return 0;
}
//获取DC与AC系数
dc_buf = (unsigned char)((priv->component_infos->DCT[0] + 512) / 4.0);
fwrite(&dc_buf, 1, 1, DCFile);
ac_buf = (unsigned char)(priv->component_infos->DCT[1] + 128);
fwrite(&ac_buf, 1, 1, ACFile);
//统计每个值的出现次数
for (int i = 0; i < 256; i++)
{
if (dc_buf == i)
dc_freq[i]++;
//printf("add dc succeed\n");
if (ac_buf == i)
ac_freq[i]++;
//printf("add ac succeed\n");
}
convert_to_pixfmt(priv);
priv->plane[0] += bytes_per_mcu[0];
priv->plane[1] += bytes_per_mcu[1];
priv->plane[2] += bytes_per_mcu[2];
if (priv->restarts_to_go>0)
{
priv->restarts_to_go--;
if (priv->restarts_to_go == 0)
{
priv->stream -= (priv->nbits_in_reservoir/8);
resync(priv);
if (find_next_rst_marker(priv) < 0)
return -1;
}
}
}
}
#if TRACE
fprintf(p_trace,"Input file size: %d\n", priv->stream_length+2);
fprintf(p_trace,"Input bytes actually read: %d\n", priv->stream - priv->stream_begin + 2);
fflush(p_trace);
#endif
for (int i = 0; i < priv->width * priv->height / 32; i++)
{
fwrite(&uv_buf, 1, 1, DCFile);
fwrite(&uv_buf, 1, 1, ACFile);
}
fclose(DCFile);
fclose(ACFile);
//计算概率分布
for (int i = 0; i < 256; i++)
{
dc_freq[i] = dc_freq[i] / (priv->width * priv->height / 64);
ac_freq[i] = ac_freq[i] / (priv->width * priv->height / 64);
}
//输出概率分布
FILE* ACfre = fopen("ac_freq.txt", "wb");
if (ACfre == 0)
{
printf("Fail to open ac_freq.txt\n");
return 0;
}
FILE* DCfre = fopen("dc_freq.txt", "wb");
if (DCfre == 0)
{
printf("Fail to open dc_freq.txt\n");
return 0;
}
return 0;
}
输出的dc图像:
输出的ac图像:
(五)统计概率
int file_length = 0;
FILE* DC_file = fopen("DC.yuv", "wb");
FILE* AC_file = fopen("AC.yuv", "wb");
unsigned char dc;
unsigned char ac;
dc = (unsigned char)((priv->component_infos->DCT[0] + 512.0) / 4);//DC系数的取值范围[-512,511],转换为[0,255]的范围
ac = (unsigned char)(priv->component_infos->DCT[1] + 128);//AC系数的取值范围为[-128,127],将其转换为[0,255]的范围
fwrite(&dc, 1, 1, DC_file);
fwrite(&ac, 1, 1, AC_file);
file_length++;
unsigned char uv = 128;
for (int i = 0; i < file_length / 4 * 2; i++) {
fwrite(&uv, 1, 1, DC_file);
fwrite(&uv, 1, 1, AC_file);
}
fclose(DC_file);
fclose(AC_file);
得到DC,AC图像概率: