JPEG原理分析及JPEG解码器的调试

实验目的

掌握JPEG编解码系统的基本原理。初步掌握复杂的数据压缩算法实现,并能根据理论分析需要实现所对应数据的输出。

方法解释

JPEG文件格式

在这里插入图片描述

SOI

在这里插入图片描述

APP0

在这里插入图片描述

DQT

在这里插入图片描述

SOF0

在这里插入图片描述

DHT

在这里插入图片描述

SOS

在这里插入图片描述

EOI

在这里插入图片描述

JPEG编码原理

在这里插入图片描述

零偏置(Level Offset)

        对于灰度级是2n的像素,通过减去2n-1,将无符号的整数值变成有符号数。
                对于n=8,即将0 ~ 255的值域,通过减去128,转换为
                值域在-128~127之间的值。
        目的:使像素的绝对值出现3位10进制的概率大大减少

DCT变换

对每个单独的彩色图像分量,把整个分量图像分成8×8的图像块,如图所示,并作为两维离散余弦变换DCT的输入。
在这里插入图片描述

量化

根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化
在这里插入图片描述

对DC系数编码

DC系数的差分编码

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

DC系数的熵编码(huffman编码)

对DIFF用Huffman编码:分成类别,类似指数Golomb编码
➢类别ID:一元码编码
➢ 类内索引:采用定长码
在这里插入图片描述

对AC系数编码

AC系数的Z字扫描

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

AC系数的游程编码、熵编码(huffman编码)

在这里插入图片描述在这里插入图片描述

实验步骤

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

参数项目设置
在这里插入图片描述
修改load.jpeg中的write_yuv函数,添加注释代码以输出yuv文件

/**
 * Save a buffer in three files (.Y, .U, .V) useable by yuvsplittoppm
 */
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);
    fopen_s(&F,temp, "wb");
    fwrite(components[0], width, height, F);
    fclose(F);
    snprintf(temp, 1024, "%s.U", filename);
    fopen_s(&F,temp, "wb");
    fwrite(components[1], width * height / 4, 1, F);
    fclose(F);
    snprintf(temp, 1024, "%s.V", filename);
    fopen_s(&F,temp, "wb");
    fwrite(components[2], width * height / 4, 1, F);
    fclose(F);
    
	//输出yuv文件
    snprintf(temp, 1024, "%s.yuv", filename);
    fopen_s(&F, 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);

}

输入test.jpg如下图
在这里插入图片描述
输出test.yuv,并用YUV viewer打开
在这里插入图片描述
在这里插入图片描述

2. 程序调试过程中,应做到:

理解程序设计的整体框架

理解三个结构体的设计目的

struct huffman_table

存储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];
};
struct component

储存8×8图像块中解码用表的信息

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

struct jdec_private

存储JPEG数据流

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

	
};

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

会打开和关闭TRACE

TRACE置1,就是打开;置0,就是关闭

#define TRACE 1
会根据自己的要求修改TRACE

(若TRACE置0,则以下代码就会标灰色,变成注释一样的存在
若置1,则会以有效代码存在

#if TRACE
    fopen_s(&p_trace,TRACEFILE, "w");
    if (p_trace == NULL)
    {
        printf("trace file open error!");
    }
#endif


在本程序中,打开TRACE,运行程序就会输出trace_jpeg.txt默认包含解码结构信息和具体huffman表

3.以txt文件输出所有的量化矩阵和所有的HUFFMAN码表

trace_jpeg.txt中已有huffman表,只需修改代码,同时输出量化表

修改tinyjpeg.c中parse_DQT函数,添加如下代码

	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
/*所添加代码<--*/
#if TRACE
        fprintf(p_trace, "Quantization_table [%d]:\n", qi);
        fflush(p_trace);
#endif
/*-->所添加代码*/
        table = priv->Q_tables[qi];
        build_quantization_table(table, stream);
        stream += 64;
    }

修改tinyjpeg.c中build_quantization_table函数,添加如下代码

    for (i = 0; i < 8; i++) {
        for (j = 0; j < 8; j++) {
/*所添加代码<--*/
#if TRACE
            fprintf(p_trace, "%d\t", ref_table[*zz]);
            fflush(p_trace);
            if (j == 7)
            {
                fprintf(p_trace, "\n");
                fflush(p_trace);
            }
#endif
/*-->所添加代码*/
            *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];
        }
    }

运行程序,输出trace_jpeg.txt

> Unknown marker e0
> DQT marker
Quantization_table [0]:
2	1	1	2	2	4	5	6	
1	1	1	2	3	6	6	6	
1	1	2	2	4	6	7	6	
1	2	2	3	5	9	8	6	
2	2	4	6	7	11	10	8	
2	4	6	6	8	10	11	9	
5	6	8	9	10	12	12	10	
7	9	10	10	11	10	10	10	
< DQT marker
> DQT marker
Quantization_table [1]:
2	2	2	5	10	10	10	10	
2	2	3	7	10	10	10	10	
2	3	6	10	10	10	10	10	
5	7	10	10	10	10	10	10	
10	10	10	10	10	10	10	10	
10	10	10	10	10	10	10	10	
10	10	10	10	10	10	10	10	
10	10	10	10	10	10	10	10	
< DQT marker
> SOF marker
> SOF marker
Size:1024x1024 nr_components:3 (YCbCr)  precision:8
Component:1  factor:1x1  Quantization table:0
Component:2  factor:1x1  Quantization table:1
Component:3  factor:1x1  Quantization table:1
< SOF marker
> DHT marker (length=27)
Huffman table DC[0] length=10
val=04 code=00000000 codesize=02
val=05 code=00000001 codesize=02
val=06 code=00000002 codesize=02
val=03 code=00000006 codesize=03
val=02 code=0000000e codesize=04
val=01 code=0000001e codesize=05
val=00 code=0000003e codesize=06
val=09 code=0000007e codesize=07
val=07 code=000000fe codesize=08
val=08 code=000001fe codesize=09
< DHT marker
> DHT marker (length=60)
Huffman table AC[0] length=43
val=00 code=00000000 codesize=02
val=01 code=00000002 codesize=03
val=03 code=00000003 codesize=03
val=02 code=00000008 codesize=04
val=04 code=00000009 codesize=04
val=05 code=0000000a codesize=04
val=11 code=0000000b codesize=04
val=21 code=0000000c codesize=04
val=22 code=0000001a codesize=05
val=31 code=0000001b codesize=05
val=61 code=0000001c codesize=05
val=06 code=0000003a codesize=06
val=12 code=0000003b codesize=06
val=a1 code=0000003c codesize=06
val=32 code=0000007a codesize=07
val=41 code=0000007b codesize=07
val=62 code=0000007c codesize=07
val=13 code=000000fa codesize=08
val=51 code=000000fb codesize=08
val=23 code=000001f8 codesize=09
val=42 code=000001f9 codesize=09
val=71 code=000001fa codesize=09
val=81 code=000001fb codesize=09
val=91 code=000001fc codesize=09
val=15 code=000003fa codesize=10
val=52 code=000003fb codesize=10
val=63 code=000003fc codesize=10
val=07 code=000007fa codesize=11
val=14 code=000007fb codesize=11
val=33 code=000007fc codesize=11
val=53 code=000007fd codesize=11
val=16 code=00000ffc codesize=12
val=43 code=00000ffd codesize=12
val=08 code=00001ffc codesize=13
val=b1 code=00001ffd codesize=13
val=34 code=00003ffc codesize=14
val=c1 code=00003ffd codesize=14
val=24 code=00007ffc codesize=15
val=d1 code=0000fffa codesize=16
val=09 code=0000fffb codesize=16
val=72 code=0000fffc codesize=16
val=f0 code=0000fffd codesize=16
val=a2 code=0000fffe codesize=16
< DHT marker
> DHT marker (length=28)
Huffman table DC[1] length=11
val=04 code=00000000 codesize=02
val=05 code=00000001 codesize=02
val=06 code=00000002 codesize=02
val=03 code=00000006 codesize=03
val=02 code=0000000e codesize=04
val=01 code=0000001e codesize=05
val=00 code=0000003e codesize=06
val=07 code=0000007e codesize=07
val=0a code=000000fe codesize=08
val=09 code=000001fe codesize=09
val=08 code=000003fe codesize=10
< DHT marker
> DHT marker (length=45)
Huffman table AC[1] length=28
val=00 code=00000000 codesize=02
val=04 code=00000001 codesize=02
val=01 code=00000004 codesize=03
val=02 code=00000005 codesize=03
val=03 code=00000006 codesize=03
val=31 code=0000001c codesize=05
val=61 code=0000001d codesize=05
val=11 code=0000003c codesize=06
val=12 code=0000003d codesize=06
val=05 code=0000007c codesize=07
val=21 code=0000007d codesize=07
val=13 code=000000fc codesize=08
val=14 code=000000fd codesize=08
val=41 code=000000fe codesize=08
val=51 code=000001fe codesize=09
val=06 code=000007fc codesize=11
val=22 code=000007fd codesize=11
val=32 code=000007fe codesize=11
val=07 code=00001ffc codesize=13
val=15 code=00001ffd codesize=13
val=42 code=00001ffe codesize=13
val=08 code=0000fff8 codesize=16
val=71 code=0000fff9 codesize=16
val=23 code=0000fffa codesize=16
val=24 code=0000fffb codesize=16
val=33 code=0000fffc codesize=16
val=81 code=0000fffd codesize=16
val=a1 code=0000fffe codesize=16
< DHT marker
> SOS marker
ComponentId:1  tableAC:0 tableDC:0
ComponentId:2  tableAC:1 tableDC:1
ComponentId:3  tableAC:1 tableDC:1
< SOS marker
Use decode 1x1 sampling
Input file size: 170416
Input bytes actually read: 170415

4&5.输出DC图像并统计其概率分布,输出某一个AC值图像并统计其概率分布

在得到的DCT数组中存储了图像DCT变换后的数据,将这些数据输出即可,其中DCT[0]为图像的直流分量,其余为交流分量,实验中取DCT[1]作为输出的AC分量值。
对于8x8的宏块,取每一个宏块DCT变换后左上角的第一个值为DC分量,输出图像的大小为128x128(原图像为1024x1024)。
为了便于观察,将DC分量的值(范围-512~512)调整到 0 ~ 255。AC分量的值较小,+128处理。
修改tinyjpeg.c中tinyjpeg_decode函数,添加如下代码

/****在tinyjpeg.c文件头部定义全局变量,文件指针
 	*FILE* dcImage;
 	*FILE* acImage;
	*/
	......
	//添加start
    unsigned char* dc_buffer, * ac_buffer, * p_dc, * p_ac, * dcbuffer_del, * acbuffer_del;

    fopen_s(&dcImage,"test_dc.yuv","wb");
    fopen_s(&acImage,"test_ac.yuv", "wb");
    //添加end
    if (setjmp(priv->jump_state))
        return -1;
    ......
    //添加start
    dc_buffer = (unsigned char*)malloc(sizeof(unsigned char) * (priv->height / ystride_by_mcu) * (priv->width / xstride_by_mcu) * 3 / 2);
    ac_buffer = (unsigned char*)malloc(sizeof(unsigned char) * (priv->height / ystride_by_mcu) * (priv->width / xstride_by_mcu) * 3 / 2);
    p_dc = dc_buffer;
    p_ac = ac_buffer;
	//添加end

    for (y = 0; y < priv->height / ystride_by_mcu; y++)
    {
    ......
                        if (find_next_rst_marker(priv) < 0)
                        return -1;
                }
            }
            //添加start
            *p_dc = (unsigned char)((priv->component_infos[cY].DCT[0] + 512) / 4);
            *p_ac = (unsigned char)(priv->component_infos[cY].DCT[1] + 128);

            p_dc++;
            p_ac++;
            //添加end
        }

    }
	//添加start
    for (int i = 0; i < ((priv->height / ystride_by_mcu) * (priv->width / xstride_by_mcu) / 2); i++)
    {
        *p_dc = (unsigned char)128;
        *p_ac = (unsigned char)128;
        p_dc++;
        p_ac++;
    }
    fwrite(dc_buffer, sizeof(unsigned char), (priv->height / ystride_by_mcu)* (priv->width / xstride_by_mcu) * 3 / 2, dcImage);
    fwrite(ac_buffer, sizeof(unsigned char), (priv->height / ystride_by_mcu)* (priv->width / xstride_by_mcu) * 3 / 2, acImage);
    fclose(dcImage);
    fclose(acImage);
    //添加end

#if TRACE
    fprintf(p_trace, "Input file size: %d\n", priv->stream_length + 2);
    ......

输出为
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值