数据压缩实验五——JPEG解码器
一、实验目的
掌握JPEG编解码系统的基本原理。初步掌握复杂的数据压缩算法实现,并能根据理论分析需要实现所对应数据的输出。
二、实验内容
1.JPEG编解码原理
JPEG编码的过程如上图所示。解码是编码的逆过程。
- JPEG格式
打开一个JPEG文件:
三、实验步骤
代码实现:
在write_yuv中添加的代码
static void write_yuv(const char *filename, int width, int height, unsigned char **components)
{
FILE *F;
char temp[1024];
snprintf(temp, 1024, "%s.Y", filename);
F = fopen(temp, "wb");
fwrite(components[0], width, height, F);
fclose(F);
snprintf(temp, 1024, "%s.U", filename);
F = fopen(temp, "wb");
fwrite(components[1], width*height/4, 1, F);
fclose(F);
snprintf(temp, 1024, "%s.V", filename);
F = fopen(temp, "wb");
fwrite(components[2], width*height/4, 1, F);
fclose(F);
//添加的代码
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);
}
将JPG转化为yuv打开
三个重要结构:
struct huffman_table用于存储haffman表
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];
};
struct component 用于存储当前8×8像块中有关解码的信息
struct component
{
unsigned int Hfactor; // 水平采样因子
unsigned int Vfactor; // 垂直采样因子
float* Q_table; // 指向该8×8块使用的量化表
struct huffman_table *AC_table; // 指向该块使用的AC Huffman表
struct huffman_table *DC_table; // 指向该块使用的DC Huffman表
short int previous_DC; // 前一个块的直流DCT系数
short int DCT[64]; // DCT系数数组
#if SANITY_CHECK
unsigned int cid;
#endif
};
struct jdec_private定义了指向三个components的指针和三个components结构体;定义了图像的宽高、码流长度、始末指针,还有三张量化表(实际只用到两张,亮度+色度)、DC Huffman表和AC Huffman表各四张。
struct jdec_private
{
/* Public variables */
uint8_t *components[COMPONENTS]; /* 分别指向YUV三个分量的三个指针 */
unsigned int width, height; /* 图像宽高 */
unsigned int flags;
/* Private variables */
const unsigned char *stream_begin, *stream_end;
unsigned int stream_length;
const unsigned char *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];
};
TRACE:将TRACE设置为1的时候打开,设置为0的时候关闭;
在代码中任意地方进行编译TRACE文件输出调试信息,方便检查。
输出:
static void build_quantization_table(float *qtable, const unsigned char *ref_table)
{
/* Taken from libjpeg. Copyright Independent JPEG Group's LLM idct.
* For float AA&N IDCT method, divisors are equal to quantization
* coefficients scaled by scalefactor[row]*scalefactor[col], where
* scalefactor[0] = 1
* scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7
* We apply a further scale factor of 8.
* What's actually stored is 1/divisor so that the inner loop can
* use a multiplication rather than a division.
*/
int i, j;
//修改
int m,n;
static const double aanscalefactor[8] = {
1.0, 1.387039845, 1.306562965, 1.175875602,
1.0, 0.785694958, 0.541196100, 0.275899379
};
const unsigned char *zz = zigzag,*zz1=zigzag;
for (i=0; i<8; i++) {
for (j=0; j<8; j++) {
*qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];
}
}
#if TRACE
for(m=0;m<8;m++)
{
for(n=0;n<8;n++)
{
fprintf(p_trace,"%d ",ref_table[*zz1++]);
}
fprintf(p_trace,"\n");
}
#endif
}
量化表:
huffman表节选:
输出DC,AC图像
/* tinyjpeg.h中添加 */
FILE* dcImgFilePtr; // DC图像文件指针
FILE* acImgFilePtr; // AC图像文件指针
tinyjpeg.c中添加
int tinyjpeg_decode(struct jdec_private* priv, int pixfmt)
{
unsigned char* dcImgBuff;
unsigned char* acImgBuff;
unsigned char* uvBuff = 128;
int count = 0;
for (y = 0; y < priv->height / ystride_by_mcu; y++) {
...
for (x = 0; x < priv->width; x += xstride_by_mcu) {
decode_MCU(priv);
dcImgBuff = (unsigned char)((priv->component_infos->DCT[0] + 512.0) / 4 + 0.5); // DCT[0]为DC系数;DC系数范围-512~512;变换到0~255
acImgBuff = (unsigned char)(priv->component_infos->DCT[1] + 128); // 选取DCT[1]作为AC的observation;+128便于观察
fwrite(&dcImgBuff, 1, 1, dcImgFilePtr);
fwrite(&acImgBuff, 1, 1, acImgFilePtr);
count++;
...
}
}
}
}
for (int i = 0; i < count / 4 * 2; i++) {
fwrite(&uvBuff, sizeof(unsigned char), 1, dcImgFilePtr);
fwrite(&uvBuff, sizeof(unsigned char), 1, acImgFilePtr);
}
return 0;
}
loadjpeg.c中添加
int main(int argc, char *argv[]) {
///
const char* dcImgFileName = "test_decoded_dc.yuv"; // DC图像文件名
const char* acImgFileName = "test_decoded_ac.yuv"; // AC图像文件名
fopen_s(&dcImgFilePtr, dcImgFileName, "wb"); // 打开DC图像文件
fopen_s(&acImgFilePtr, acImgFileName, "wb"); // 打开AC图像文件
/* Addition ended */
fclose(dcImgFilePtr);
fclose(acImgFilePtr);
return 0;
}