数据压缩实验五
实验原理
jpeg编解码原理
jpeg文件格式
8*8块编码
零偏置
对于灰度级是2^n的像素,通过减去2^n-1,将无符号的整数值变成有符号数,对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值。
目的:使像素的绝对值出现3位10进制的概率大大减少。
DCT变换量化
中平型均匀量化,量化步距是按照系数所在的位置颜色分量来确定
因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值。根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化。如果原始图象中细节丰富,则去掉的数据较多,量化后的系数与量化前差别。反之,细节少的原始图象在压缩时去掉的数据少些。
ac游程编码
在JPEG和MPEG编码中规定为:(run, level)表示连续run个0,后面跟值为level的系数如:0,2,0,0,3,0,-4,0,0,0,-6,0,0,5,7 表示为(1, 2), (2, 3) ,...
编码:
Run: 最多15个,用4位表示RRRR
Level:类似DC分成16个类别,用4位表示SSSS表示类别号,类内索引
对(RRRR, SSSS)联合用Huffman编码 对类内索引用定长码编码
ac系数的之字形扫描
DC差分编码
8×8图像块经过DCT变换之后得到的DC直流系数有两个特点:系数的数值比较大、相邻8×8图像块的DC系数值变化不大:冗余
根据这个特点,JPEG算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化DC系数的差值 DIFF进行编码:
DIFF=DCk- DCk-1
解码重构
与编码相反::
解码Huffman数据、解码DC差值、重构量化后的系数、DCT逆变换、丢弃填充的行/列、反0偏置、对丢失的CbCr分量差值(下采样的逆过程) YCbCr -》 RGB
实验步骤1.逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供YUVViewer观看的YUV文件。
2. 程序调试过程中,应做到:
理解程序设计的整体框架
理解三个结构体的设计目的
struct huffman_table
struct component
struct jdec_private
理解在视音频编解码调试中TRACE的目的和含义
会打开和关闭TRACE
会根据自己的要求修改TRACE
3.以txt文件输出所有的量化矩阵和所有的HUFFMAN码表。
4. 输出DC图像并经过huffman统计其概率分布(使用第三个实验中的Huffman编码器)。
5. 输出某一个AC值图像并统计其概率分布(使用第三个实验中的Huffman编码器)
实验代码及分析
1、输出一个yuv文件
yuv输出的时候定义部分和yuv420,即分别输出y、u、v、文件的过程基本相同,照此进行修改
//LOADJPEG.C
//将三个yuv文件在一个文件中输出
static void write_yuv2(const char *filename, int width, int height, unsigned char **components)
{
FILE *F;
char temp[1024];
snprintf(temp, 1024, "%s.YUV", filename);
F = fopen(temp, "wb");
fwrite(components[0], width, height, F);
fwrite(components[1], width*height / 4, 1, F);
fwrite(components[2], width*height / 4, 1, F);
fclose(F);
}
/* Save it */
switch (output_format)
{
case TINYJPEG_FMT_RGB24:
case TINYJPEG_FMT_BGR24:
write_tga(outfilename, output_format, width, height, components);
break;
case TINYJPEG_FMT_YUV420P:
write_yuv(outfilename, width, height, components);
break;
case TINYJPEG_FMT_GREY:
write_pgm(outfilename, width, height, components);
break;
case TINYJPEG_FMT_YUV:
write_yuv2(outfilename, width, height, components);
break;
}
static void usage(void)
{
fprintf(stderr, "Usage: loadjpeg [options] <input_filename.jpeg> <format> <output_filename>\n");
fprintf(stderr, "options:\n");
fprintf(stderr, " --benchmark - Convert 1000 times the same image\n");
fprintf(stderr, "format:\n");
fprintf(stderr, " yuv420p - output 3 files .Y,.U,.V\n");
fprintf(stderr, " rgb24 - output a .tga image\n");
fprintf(stderr, " bgr24 - output a .tga image\n");
fprintf(stderr, " gray - output a .pgm image\n");
fprintf(stderr, " yuv - output a .yuv image\n");
exit(1);
}
主函数中if (strcmp(argv[current_argument+1],"yuv420p")==0)
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 if (strcmp(argv[current_argument + 1], "yuv") == 0)
output_format = TINYJPEG_FMT_YUV;
else
exitmessage("Bad format: need to be one of yuv420p, rgb24, bgr24, grey\n");
output_filename = argv[current_argument+2];
//TINYJPEG.H
enum tinyjpeg_fmt {
TINYJPEG_FMT_GREY = 1,
TINYJPEG_FMT_BGR24,
TINYJPEG_FMT_RGB24,
TINYJPEG_FMT_YUV420P,
TINYJPEG_FMT_YUV,};
函数中的内容和YUV420中的相同//TINYJPEG.C
case TINYJPEG_FMT_YUV:
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;
2、结构体分析
component:存放MCU用来作为最小的数据块访问数据,其中Hfactor表示水平取样,Vfactor表示垂直
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
};
jade_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];
};
huffman_table:用来输出ac和dc系数的huffman表
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];
};
3、trace
trace是用来输出文件中间信息的,想要关闭trace只需要在定义trace的时候也就是在tinyjpeg.h文件中另#define TRACE 0
static int parse_JFIF(struct jdec_private *priv, const unsigned char *stream)
{
int chuck_len;
int marker;
int sos_marker_found = 0;
int dht_marker_found = 0;
const unsigned char *next_chunck;
/* Parse marker */
while (!sos_marker_found)
{
if (*stream++ != 0xff)
goto bogus_jpeg_format;
/* Skip any padding ff byte (this is normal) */
while (*stream == 0xff)
stream++;
marker = *stream++;
chuck_len = be16_to_cpu(stream);
next_chunck = stream + chuck_len;
switch (marker)
{
case SOF:
if (parse_SOF(priv, stream) < 0)
return -1;
break;
case DQT:
if (parse_DQT(priv, stream) < 0)
return -1;
break;
case SOS:
if (parse_SOS(priv, stream) < 0)
return -1;
sos_marker_found = 1;
break;
case DHT:
if (parse_DHT(priv, stream) < 0)
return -1;
dht_marker_found = 1;
break;
case DRI:
if (parse_DRI(priv, stream) < 0)
return -1;
break;
default:
#if TRACE
fprintf(p_trace,"> Unknown marker %2.2x\n", marker);
fflush(p_trace);
#endif
break;
}
stream = next_chunck;
}
if (!dht_marker_found) {
#if TRACE
fprintf(p_trace,"No Huffman table loaded, using the default one\n");
fflush(p_trace);
#endif
build_default_huffman_tables(priv);
}
#ifdef SANITY_CHECK
if ( (priv->component_infos[cY].Hfactor < priv->component_infos[cCb].Hfactor)
|| (priv->component_infos[cY].Hfactor < priv->component_infos[cCr].Hfactor))
snprintf(error_string, sizeof(error_string),"Horizontal sampling factor for Y should be greater than horitontal sampling factor for Cb or Cr\n");
if ( (priv->component_infos[cY].Vfactor < priv->component_infos[cCb].Vfactor)
|| (priv->component_infos[cY].Vfactor < priv->component_infos[cCr].Vfactor))
snprintf(error_string, sizeof(error_string),"Vertical sampling factor for Y should be greater than vertical sampling factor for Cb or Cr\n");
if ( (priv->component_infos[cCb].Hfactor!=1)
|| (priv->component_infos[cCr].Hfactor!=1)
|| (priv->component_infos[cCb].Vfactor!=1)
|| (priv->component_infos[cCr].Vfactor!=1))
snprintf(error_string, sizeof(error_string),"Sampling other than 1x1 for Cr and Cb is not supported");
#endif
return 0;
bogus_jpeg_format:
#if TRACE
fprintf(p_trace,"Bogus jpeg format\n");
fflush(p_trace);
#endif
return -1;
}
首先在tinyjpeg.h文件中定义一个控制huffman码表的变量:tables
#define TABLES 1
函数dht下,输出acdc类别
if (index & 0xf0 )
{
#if TABLES
fprintf(AC_TABLE, "Huffman table AC[%d] length=%d\n", index & 0xf, count);
fflush(AC_TABLE);
#endif
aqi=0; //控制输出的是ac还是dc
build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);
}
else
{
#if TABLES
fprintf(DC_TABLE, "Huffman table DC[%d] length=%d\n", index & 0xf, count);
fflush(DC_TABLE);
#endif
aqi=1;
build_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]);
}
输出量化表
for(i=0;i<64;i++)
{
if((!(i%8)))
{
fprintf(Q_table,"\n%f",table[i]);
}
else
{
fprintf(Q_table," %f",table[i]);
}
}
fprintf(Q_table,"\n");
输出码表
fprintf(AC_TABLE,"val=%2.2x code=%8.8x codesize=%2.2d\n",val,code,code_size);
#if TABLES
if(aqi==0)
{
fflush(AC_TABLE);
}
else{
#endif
fprintf(DC_TABLE,"val=%2.2x code=%8.8x codesize=%2.2d\n",val,code,code_size);
fflush(AC_TABLE);
}
4、输出ac、dc图像
在tinyjpeg.h文件中添加FILE *AC_table和*DC_table的定义
在主函数中开辟空间
char temp[1024]; snprintf(temp, 1024, "%sDC.Y", output_filename);
DC_FILE = fopen(temp, "wb");
snprintf(temp, 1024, "%sAC.Y", output_filename);
AC_FILE = fopen(temp, "wb");
//tinyjpeg.c文件 dc分量需要被压缩,写文件的时候是8*8的块所以输出文件大小变为128*128,ac的值比较小,所以加128之后再输出
DC[0]=(priv->component_infos->DCT[0]+512.0)/4;
DCimage[0]=(unsigned char)(DC[0]+0.5);
fwrite(DCimage,1,1,DC_FILE);
ACimage[0]=(unsigned char)(priv->component_infos->DCT[1]+128);
fwrite(ACimage,1,1,AC_FILE);
实验结果
上一篇: (超详细)mirai机器人(Java)
下一篇: Hive文件存储格式和数据压缩