您的位置:首页 > 其它

实验报告5_JPEG编解码原理及程序调试

2017-06-05 21:54 447 查看

一.实验原理

JPEG是一种有损压缩格式,能够将图像压缩在很小的储存空间,图像中重复或不重要的资料会被丢失,因此容易造成图像数据的损伤。JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域。

JPEG编码流程:



JPEG编解码流程:






1.编码过程详解:

(1)零偏置:

对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数。

eg.对于n=8,即将0~255的值域,通过减去128,转换为值域在-128~127之间的值

目的:使像素的绝对值出现3位10进制的概率大大减少


(2)8x8DCT变换


将图像分为8x8的子块,边缘不满足8x8的部分则用边缘像素填充,然后对每一个子块进行DCT变换。

DCT变换公式:



(3)对DCT系数进行量化

量化采用中平型均匀量化器,量化步距是按照系数所在位置颜色分量来确定。

因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值

根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化



(4)编码

DC编码:

8×8图像块经过DCT变换之后得到的DC直流系数有两个特点

-系数的数值比较大

-相邻8×8图像块的DC系数值变化不大:冗余

根据这个特点,JPEG算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化DC系数的差值DIFF进行编码:



对DIFF用Huffman编码:分成类别

-类别ID:一元码编码

-类内索引:采用定长码





AC编码(游程编码):

由于经DCT变换后,系数大多数集中在左上角,即低频分量区,因此采用Z字形按频率的高低顺序读出,可以出现很多连零的机会。可以使用游程编码。尤其在最后,如果都是零,给出EOB (End of Block)即可。



在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编码

-对类内索引用定长码编码

2.JPEG数据格式

JPEG语法结构中定义了一系列标记(Markers)

-均以0xFF开始,后跟1字节的标记标识符和2字节的标记长度以及该标记所对应的payload

-标记长度部分高位在前,低位在后,不包含该标记的头两个字节

-熵编码部分的数据在0xFF后由编码器插入0x00,解码器解码时跳过此字节不予处理

-SOI( Start Of Image)和EOI( End Of Image)标记没有payload



可在下例中找到对应:



二.实验流程

(1)逐步调试JPEG解码器程序。将输入的JPG文件进行解码,将输出文件保存为可供YUVViewer观看的YUV文件。

(2)理解程序设计的整体框架

(3)理解三个结构体的设计目的:struct huffman_table, struct component , struct jdec_private

(4)理解在视音频编码调试中 TRACE TRACE的目和含义

         -会打开和关闭 TRACE TRACE

         -会根据自己的要求修改 TRACE TRACE

(5)以txt文件输出所有的量化矩阵和所有的HUFFMAN码表。

(6)输出DC图像并经过huffman统计其概率分布(使用第三个实验中的 Huffman Huffman Huffman编码器)。

(7)输出某一个 AC 值图像并统计其概率分布(使用第三个实验中的 Huffman HuffmanHuffman 编码器)。

三.关键代码分析

1.将输出文件保存成YUV格式

tinyjpeg.h

enum tinyjpeg_fmt{
TINYJPEG_FMT_GREY = 1,
TINYJPEG_FMT_BGR24,
TINYJPEG_FMT_RGB24,
TINYJPEG_FMT_YUV420P,
TINYJPEG_FMT_YUV,//add by hyw
};
loadjpeg.c

main函数中:

……
int output_format = TINYJPEG_FMT_YUV;//将输出格式设置为yuv
……
input_filename = argv[current_argument];
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)//add by hyw
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];
 
int load_multiple_times(const char *filename, const char *outfilename, int output_format)
{……
switch (output_format)
{
case TINYJPEG_FMT_RGB24:
case TINYJPEG_FMT_BGR24:
write_tga(outfilename, output_format, widt
bc0a
h, 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_pgm(outfilename, width, height, components);//add by hyw
break;
}
……
}


int convert_one_image(const char *infilename, const char *outfilename, int output_format)
{……
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_yuv_file(outfilename, width, height, components);//add by hyw
break;
}
……
}


int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)
{……
switch (pixfmt) {
case TINYJPEG_FMT_YUV420://add by hyw
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;
……


//自己定义的写入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],1,width* height, F);
fwrite(components[1], 1, width* height/4, F);
fwrite(components[2], 1, width* height/4, F);

}


2.将量化矩阵和Huffman表输出到txt文件

tinyjpeg.h

#define RTXT 1//add by hyw
#define RTXTFILE "result_jpeg.txt"//add by hyw


tinyjpeg.c

输出huffman码表:

static void build_huffman_table(const unsigned char *bits, const unsigned char *vals, struct huffman_table *table)
{
......
#if TRACE
fprintf(p_trace,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
fflush(p_trace);
#endif
#if RTXT
fprintf(p_rtxt, "val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size);
fflush(p_rtxt);
#endif
......
}


输出量化表:

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;
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;

for (i=0; i<8; i++) {
for (j=0; j<8; j++) {
#if RTXT
fprintf(p_rtxt, "%d ", ref_table[*zz]);//输出的量化矩阵为之字形扫描顺序
fflush(p_rtxt);
#endif
 *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j]; } } #if RTXT

     fprintf(p_rtxt, "\n");

     fflush(p_rtxt);

  #endif}



DQT码表头尾:

static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{
int qi;
float *table;
const unsigned char *dqt_block_end;
#if TRACE
fprintf(p_trace,"> DQT marker\n");
fflush(p_trace);
#endif

#if RTXT//add by hyw
fprintf(p_rtxt, "> DQT marker\n");
fflush(p_rtxt);
#endif
dqt_block_end = stream + be16_to_cpu(stream);
stream += 2;	/* Skip length */

while (stream < dqt_block_end)
{
qi = *stream++;
#if SANITY_CHECK
if (qi>>4)
snprintf(error_string, sizeof(error_string),"16 bits quantization table is not supported\n");
if (qi>4)
snprintf(error_string, sizeof(error_string),"No more 4 quantization table is supported (got %d)\n", qi);
#endif
table = priv->Q_tables[qi];
build_quantization_table(table, stream);
stream += 64;
}
#if TRACE
fprintf(p_trace,"< DQT marker\n");
fflush(p_trace);
#endif

#if RTXT//add by hyw
fprintf(p_rtxt, "< DQT marker\n");
fflush(p_rtxt);
#endif
return 0;
}


3.输出DC图像和某一AC的图像:

tinyjpeg.h

void DC(struct jdec_private *priv);//DC
void AC(struct jdec_private *priv);//AC
void outputDCAC(struct jdec_private *priv);//输出图像


tinyjpeg.c

int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)
{
..................................................
priv->DCImage = (int *)malloc(sizeof(int)*priv->width * priv->height/64);//edit by lee 2017 5 18
priv->ACImage = (int *)malloc(sizeof(int)*priv->width * priv->height/64);//edit by lee 2017 5 18

outputDCAC(priv);//by hyw
.......................................
}


DC

static void DCiamge(struct jdec_private *priv)
{
static long i = 0;//i为DCT块的个数
if (i<priv->height*priv->width / 64)//8x8块
{
priv->DCImage[i] = priv->component_infos[cY].DCT[0];//cY=0
}
i++;
}
AC

static void ACiamge(struct jdec_private *priv)
{
static long int i = 0;
if (i<priv->height*priv->width / 64)
priv->ACImage[i] = priv->component_infos[cY].DCT[2];//选取DCT[64]的第二个ac系数(取值范围(1,63))
i++;
}


输出图像

static void outputDCAC(struct jdec_private *priv)
{
int i = 0;
int dcmax, dcmin;//方便归一化
int acmax, acmin;//方便归一化
unsigned char *temp;//记录归一化后的值方便输出
/*设置初值*/
dcmin = priv->DCImage[0];
dcmax = priv->DCImage[0];
acmin = priv->ACImage[0];
acmax = priv->ACImage[0];
temp = (unsigned char*)malloc(priv->height*priv->width / 64);//找出最大最小值并临时存放值
for (i = 0; i < (priv->height*priv->width / 64); i++)
{
if (priv->DCImage[i] > dcmax)
dcmax = priv->DCImage[i];
if (priv->DCImage[i] < dcmin)
dcmin = priv->DCImage[i];
if (priv->ACImage[i] > acmax)
acmax = priv->ACImage[i];
if (priv->ACImage[i] < acmin)
acmin = priv->ACImage[i];
}//找最大最小值
for (i = 0; i < (priv->height*priv->width / 64); i++)
{
temp[i] = (unsigned char) 255*(priv->DCImage[i] - dcmin) / (dcmax - dcmin);
}
fwrite(temp, 1, priv->width*priv->height / 64, DFILE);
for (i = 0; i < (priv->height*priv->width / 64); i++)
{
temp[i] = (unsigned char)255*(priv->ACImage[i] - acmin)/ (acmax - acmin);
}
fwrite(temp, 1, priv->width*priv->height / 64, AFILE);
if (temp) free(temp);
}


四.实验结果












                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: