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

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

基本思路
JPEG 是 Joint Photographic Exports Group 的英文缩写,中文称之为联合图像专家小组。该小组隶属于 ISO 国际标准化组织,主要负责定制静态数字图像的编码方法,即所谓的 JPEG算法。
JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域,网站上80%的图像都采用了JPEG压缩标准。

在这里插入图片描述
(1) 8*8分块。
DCT 变换是指对每个单独的彩色图像分量,把整个分量图像分成 8×8 的图像块,再以8x8 的图像块为一个单位进行量化和编码处理。我们可以利用 DCT 变换去相关的特性,去除冗余信息,提高编码效率。

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

(3) 量化(quantization)。
我们可以通过量化减少数据的编码位数,提高编码效率;因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值;根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较细的量化,对高频分量采取较粗的量化。

(4)DC 系数差分编码
8×8 图像块经过 DCT 变换之后得到的 DC 直流系数有两个特点:
1)系数的数值比较大
2)相邻 8×8 图像块的 DC 系数值变化不大:冗余;
根据这个特点, JPEG 算法使用了差分脉冲调制编码(DPCM)技术,对相邻图像块之间量化 DC 系数的差值 DIFF 进行编码
再对 DIFF 进行 Huffman 编码

(5) Z字形编码(zigzag scan)。
由于经 DCT 变换后,系数大多数集中在左上角,即低频分量区,因此采用 Z 字形按频率的高低顺序读出,可以出现很多连零的机会。可以使用游程编码。尤其在最后,如果都是零,给出 EOB (End of Block)即可。
在经过之字形扫描排序后的 AC 系数,存在很多连 0。为了进一步提高编码效率,因此对 AC 系数进行游程编码(RLC)处理之后,再进一步进行 Huffman 编码。

(6) AC 和 DC 系数分别进行 Huffman 编码
JPEG 中共采用了四张 Huffman 码表:亮度 DC、亮度 AC、色度 DC、色度 AC,即分别对图像的亮度和色度,直流和交流数据进行编码处理。

关键代码
// 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];
    };
    // 分量结构体:代表当前正在处理的某个分量的8x8块
    struct component
    {
    unsigned int Hfactor;//水平采样因子
    unsigned int Vfactor;//垂直采样因子
    float Q_table; / Pointer to the quantisation table to use */
    struct huffman_table *AC_table;//指向该宏块交流系数的Huffman码表
    struct huffman_table DC_table;//指向该宏块直交流系数的Huffman码表
    short int previous_DC; /
    Previous DC coefficient ///前一个块的DC系数
    short int DCT[64]; /
    DCT coef ///该块的DCT系数,其中DCT[0]为该块直流,其他为交流
    #if SANITY_CHECK
    unsigned int cid;
    #endif
    };
    // 文件解码信息结构体
    struct jdec_private
    {
    /
    Public variables */
    //分别指向YUV分量结构体的指针数组
    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;

#include <stdio.h>
#ifndef JPEGDEC_H
#define JPEGDEC_H
#define TINYJPEG_FLAGS_MJPEG_TABLE (1<<1)
#ifdef __cplusplus
extern “C” {
#endif
#define snprintf _snprintf
// 通过定义TRACE的值为0或1来打开和关闭TRACE
#define TRACE 1
#define TRACEFILE “trace_jpeg.txt”
static const unsigned char zigzag[64];
struct jdec_private;
enum tinyjpeg_fmt {
TINYJPEG_FMT_GREY = 1,
TINYJPEG_FMT_BGR24,
TINYJPEG_FMT_RGB24,
TINYJPEG_FMT_YUV420P,
};
FILE *p_trace;//add by nxn
static char error_string[256];//add by nxn
struct jdec_private *tinyjpeg_init(void);
void tinyjpeg_free(struct jdec_private *priv);
int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size);
int tinyjpeg_decode(struct jdec_private *priv, int pixel_format);
const char *tinyjpeg_get_errorstring(struct jdec_private *priv);
void tinyjpeg_get_size(struct jdec_private *priv, unsigned int *width, unsigned int *height);
int tinyjpeg_get_components(struct jdec_private *priv, unsigned char **components);
int tinyjpeg_set_components(struct jdec_private *priv, unsigned char **components, unsigned int ncomponents);
int tinyjpeg_set_flags(struct jdec_private *priv, int flags);
static void outdcac(struct jdec_private *priv, FILE *DC_image, FILE *AC_image);
static void getdcac(struct jdec_private *priv);
#ifdef __cplusplus
}
#endif
#endif

int main(int argc, char *argv[])
{
// 初始化输出文件格式为YUV420P
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;
// 通过在tinyjpeg.h文件中设置TRACE的值为1以及#if TRACE … #endif打开打开TRACE文件,TRACE是用来跟踪和记录解码过程,存放解码过程中得到的数据
#if 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)
benchmark_mode = 1;
else
break;
current_argument++;
}

if (argc < current_argument+2)
usage();

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
exitmessage(“Bad format: need to be one of yuv420p, rgb24, bgr24, grey\n”);
output_filename = argv[current_argument+2];

start_time = clock();

if (benchmark_mode)
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;
}
convert_one_image:加载一幅jpeg图像,解压缩后存出结果
int convert_one_image(const char *infilename, const char *outfilename, int output_format)
{
// 定义文件指针
FILE *fp;
// 保存文件大小
unsigned int length_of_file;
// 保存图像宽高
unsigned int width, height;
// 用来存储JPEG文件中的数据的缓冲区
unsigned char *buf;
struct jdec_private *jdec;
unsigned char *components[3];
fp = fopen(infilename, “rb”);
if (fp == NULL)
exitmessage(“Cannot open filename\n”);
// 获取文件大小
length_of_file = filesize

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值