JPEG 是Joint Photographic Experts Group(联合图像专家小组)的缩写,是第一个国际图像压缩标准。JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域。人们日常碰到的".jpeg"、''.jpg"等指代的是用JPEG算法压缩出来的静态图片文件在媒体上的封存形式,不能与JPEG压缩标准混为一谈。

一、实验内容

1、JPEG编解码原理

JPEG编码的过程如上图所示。解码是编码的逆过程。

2、JPEG文件格式

JPEG 在文件中以 Segment 的形式组织,它具有以下特点:

1.均以 0xFF 开始,即表示图像开始SOI (Start of Image)标记,后跟 1 byte 的 Marker 和 2 byte 的 Segment length(包含表示Length 本身所占用的 2 byte,不含“0xFF” + “Marker” 所占用的 2 byte);

2.采用 Motorola 序(相对于 Intel 序),即保存时高位在前,低位在后;

3.Data 部分中,0xFF 后若为 0x00,则跳过此字节不予处理;

4.一定以0xFFD9结束,表示图像结束EOI(End of Image)标记

(1)APPn,数值0xFF E1~0xFFEF
*N=1~15,数值对应0xE1~0xEF
*APPn长度(Length)
*应用细节信息(Application specific information)
*EXIF文件格式中多写入0xFF E1

(2)DQT标记段,包括一个或者多个量化表
*量化表长度。长度参数占用两个字节,他不包括前两个字节0XFF和0XDB。
*量化表数目。表数目参数占用一个字节,其中高4位为量化表的数据精确度。若高4位等于0,那么后面的量化表中的每个值占8位;若高4位等于1,那么后面的量化表中每个值占16位,低4位表示量化表的编号,为0~3.
*量化表表项。表项参数对应Z字形排列后的64个数值。

(3)DHT标记段,包括一个或多个霍夫曼表
*霍夫曼码表的长度。占用两个字节。
*每个霍夫曼码表(霍夫曼码表一般不止一个,但是绝对不多于4个)都包含4个信息:索引、类型、位表和内容编码。索引信息表示该霍夫曼表的表号,占1个字节。高四位可以是0或1,为0时指DC所用的霍夫曼表;为1时,指AC所用的霍夫曼表。低4位表示霍夫曼表的编号,值为0或1.位表是一个长为16字节的编码,其代码代数和为接下来的编码长度。内容编码信息表示每个霍夫曼码字对应的值

3、JPEG 的解码流程

(1)零偏置(level offset)

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

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

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

(2)8*8DCT变换

对每个单独的彩色图像分量,把整个分量图像分成8×8的图像块,若边缘未满8*8,则用边缘像素进行填充(不建议用黑或白像素填充的原因是可能会破坏图像的原有结构)。

DCT变换使用下式计算:

图像的低频部分集中在每个8*8块的左上角,高频部分集中在右下角。

(3)量化

因为人眼对亮度信号比对色差信号更敏感,因此使用了两种量化表:亮度量化值和色差量化值;根据人眼的视觉特性(对低频敏感,对高频不太敏感)对低频分量采取较的量化,对高频分量采取较的量化。

(4)DC系数

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

(5)AC系数

由于经过DCT变换后,系数大多集中在左上角,也就是低频分量区,因此采用Z字形扫描,按频率的高低顺序读出,这样会出现很多的连零,可以使用RLE游程编码,尤其是在最后,如果都是零,直接给出EOB(End of Block)即可
在JPEG编码中,游程编码的形式为:(run,level)
表示连续run个0后有值为level的系数
run最多15个,用4位表示RRRR
level,类似DC,
        分成16个类别,用4位SSSS表示类别号,
        类内索引
对(RRRR,SSSS)采用Huffman编码,
对类内索引采用定长编码

二、实验步骤

(1)实验用图:

SOI和EOI

 

(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];    //当码长>9时,交给该表处理
};
  • 结构体struct  component——存储当前像块中关于解码的信息

    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_CHECKunsigned int cid;
    #endif
    };
    
  • 结构体struct  jdec_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];};

(3)主要代码分析

main——输入输出文件的获取、输出格式和解码方式的选择

int main(int argc, char *argv[])
{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;
#if TRACEp_trace=fopen(TRACEFILE,"w");if (p_trace==NULL){printf("trace file open error!");}
#endifif (argc < 3)usage();current_argument = 1;while (1){if (strcmp(argv[current_argument], "--benchmark")==0)benchmark_mode = 1;elsebreak;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;elseexitmessage("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);elseconvert_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 TRACEfclose(p_trace);
#endifreturn 0;
}

解码

int convert_one_image(const char *infilename, const char *outfilename, int output_format)
{FILE *fp;unsigned int length_of_file;unsigned int width, height;unsigned char *buf;struct jdec_private *jdec;unsigned char *components[3];/* Load the Jpeg into memory */fp = fopen(infilename, "rb");if (fp == NULL)exitmessage("Cannot open filename\n");length_of_file = filesize(fp);buf = (unsigned char *)malloc(length_of_file + 4);if (buf == NULL)exitmessage("Not enough memory for loading file\n");fread(buf, length_of_file, 1, fp);fclose(fp);/* Decompress it */jdec = tinyjpeg_init();if (jdec == NULL)exitmessage("Not enough memory to alloc the structure need for decompressing\n");if (tinyjpeg_parse_header(jdec, buf, length_of_file)<0)exitmessage(tinyjpeg_get_errorstring(jdec));/* Get the size of the image */tinyjpeg_get_size(jdec, &width, &height);snprintf(error_string, sizeof(error_string),"Decoding JPEG image...\n");if (tinyjpeg_decode(jdec, output_format) < 0)exitmessage(tinyjpeg_get_errorstring(jdec));/* * Get address for each plane (not only max 3 planes is supported), and* depending of the output mode, only some components will be filled * RGB: 1 plane, YUV420P: 3 planes, GREY: 1 plane*/tinyjpeg_get_components(jdec, components);/* 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;}/* Only called this if the buffers were allocated by tinyjpeg_decode() */tinyjpeg_free(jdec);/* else called just free(jdec); */free(buf);return 0;
}

 tinyjpeg_parse_header——JPEG文件头解析

int tinyjpeg_parse_header(struct jdec_private *priv, const unsigned char *buf, unsigned int size)
{int ret;/* Identify the file */if ((buf[0] != 0xFF) || (buf[1] != SOI))    //判断是否是jpeg文件snprintf(error_string, sizeof(error_string),"Not a JPG file ?\n");priv->stream_begin = buf+2;priv->stream_length = size-2;priv->stream_end = priv->stream_begin + priv->stream_length;ret = parse_JFIF(priv, priv->stream_begin);  return ret;
}

static int parse_JFIF——解析marker标志

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 TRACEfprintf(p_trace,"> Unknown marker %2.2x\n", marker);fflush(p_trace);
#endifbreak;}stream = next_chunck;}if (!dht_marker_found) {
#if TRACEfprintf(p_trace,"No Huffman table loaded, using the default one\n");fflush(p_trace);
#endifbuild_default_huffman_tables(priv);}#ifdef SANITY_CHECKif (   (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");
#endifreturn 0;
bogus_jpeg_format:
#if TRACEfprintf(p_trace,"Bogus jpeg format\n");fflush(p_trace);
#endifreturn -1;
}

static int parse_DQT——解码量化表

static int parse_DQT(struct jdec_private *priv, const unsigned char *stream)
{int qi;float *table;const unsigned char *dqt_block_end;
#if TRACEfprintf(p_trace,"> DQT marker\n");fflush(p_trace);
#endifdqt_block_end = stream + be16_to_cpu(stream);stream += 2; /* Skip length */while (stream < dqt_block_end){qi = *stream++;
#if SANITY_CHECKif (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);
#endiftable = priv->Q_tables[qi];build_quantization_table(table, stream);stream += 64;}
#if TRACEfprintf(p_trace,"< DQT marker\n");fflush(p_trace);
#endifreturn 0;
}

static void build_quantization_table——建立量化表

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;const unsigned char *zz2 = zigzag;for (i=0; i<8; i++) {for (j=0; j<8; j++) {*qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j];}}#if TRACEfor (i=0; i<8; i++){for (j=0; j<8; j++){fprintf(p_trace,"%-6d",ref_table[*zz2++]);}fprintf(p_trace,"\n");}
#endif
}

parse_DHT——解码Huffman码表,并同时将Huffman码表写入trace文件

static int parse_DHT(struct jdec_private *priv, const unsigned char *stream)
{unsigned int count, i;unsigned char huff_bits[17];int length, index;length = be16_to_cpu(stream) - 2;stream += 2;   /* Skip length */
#if TRACEfprintf(p_trace,"> DHT marker (length=%d)\n", length);fflush(p_trace);
#endifwhile (length>0) {index = *stream++;/* We need to calculate the number of bytes 'vals' will takes */huff_bits[0] = 0;count = 0;for (i=1; i<17; i++) {huff_bits[i] = *stream++;count += huff_bits[i];}
#if SANITY_CHECKif (count >= HUFFMAN_BITS_SIZE)snprintf(error_string, sizeof(error_string),"No more than %d bytes is allowed to describe a huffman table", HUFFMAN_BITS_SIZE);if ( (index &0xf) >= HUFFMAN_TABLES)snprintf(error_string, sizeof(error_string),"No more than %d Huffman tables is supported (got %d)\n", HUFFMAN_TABLES, index&0xf);
#if TRACEfprintf(p_trace,"Huffman table %s[%d] length=%d\n", (index&0xf0)?"AC":"DC", index&0xf, count);fflush(p_trace);
#endif
#endifif (index & 0xf0 )build_huffman_table(huff_bits, stream, &priv->HTAC[index&0xf]);elsebuild_huffman_table(huff_bits, stream, &priv->HTDC[index&0xf]);length -= 1;length -= 16;length -= count;stream += count;}
#if TRACEfprintf(p_trace,"< DHT marker\n");fflush(p_trace);
#endifreturn 0;
}

tinyjpeg_decode——以mcu为单位进行解码

int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)
{unsigned int x, y, xstride_by_mcu, ystride_by_mcu;unsigned int bytes_per_blocklines[3], bytes_per_mcu[3];decode_MCU_fct decode_MCU;const decode_MCU_fct *decode_mcu_table;const convert_colorspace_fct *colorspace_array_conv;convert_colorspace_fct convert_to_pixfmt;if (setjmp(priv->jump_state))return -1;/* To keep gcc happy initialize some array */bytes_per_mcu[1] = 0;bytes_per_mcu[2] = 0;bytes_per_blocklines[1] = 0;bytes_per_blocklines[2] = 0;decode_mcu_table = decode_mcu_3comp_table;switch (pixfmt) {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;case TINYJPEG_FMT_RGB24:colorspace_array_conv = convert_colorspace_rgb24;if (priv->components[0] == NULL)priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3);bytes_per_blocklines[0] = priv->width * 3;bytes_per_mcu[0] = 3*8;break;case TINYJPEG_FMT_BGR24:colorspace_array_conv = convert_colorspace_bgr24;if (priv->components[0] == NULL)priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3);bytes_per_blocklines[0] = priv->width * 3;bytes_per_mcu[0] = 3*8;break;case TINYJPEG_FMT_GREY:decode_mcu_table = decode_mcu_1comp_table;colorspace_array_conv = convert_colorspace_grey;if (priv->components[0] == NULL)priv->components[0] = (uint8_t *)malloc(priv->width * priv->height);bytes_per_blocklines[0] = priv->width;bytes_per_mcu[0] = 8;break;default:
#if TRACEfprintf(p_trace,"Bad pixel format\n");fflush(p_trace);
#endifreturn -1;}xstride_by_mcu = ystride_by_mcu = 8;if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) {decode_MCU = decode_mcu_table[0];convert_to_pixfmt = colorspace_array_conv[0];
#if TRACEfprintf(p_trace,"Use decode 1x1 sampling\n");fflush(p_trace);
#endif} else if (priv->component_infos[cY].Hfactor == 1) {decode_MCU = decode_mcu_table[1];convert_to_pixfmt = colorspace_array_conv[1];ystride_by_mcu = 16;
#if TRACEfprintf(p_trace,"Use decode 1x2 sampling (not supported)\n");fflush(p_trace);
#endif} else if (priv->component_infos[cY].Vfactor == 2) {decode_MCU = decode_mcu_table[3];convert_to_pixfmt = colorspace_array_conv[3];xstride_by_mcu = 16;ystride_by_mcu = 16;
#if TRACE fprintf(p_trace,"Use decode 2x2 sampling\n");fflush(p_trace);
#endif} else {decode_MCU = decode_mcu_table[2];convert_to_pixfmt = colorspace_array_conv[2];xstride_by_mcu = 16;
#if TRACEfprintf(p_trace,"Use decode 2x1 sampling\n");fflush(p_trace);
#endif}resync(priv);/* Don't forget to that block can be either 8 or 16 lines */bytes_per_blocklines[0] *= ystride_by_mcu;bytes_per_blocklines[1] *= ystride_by_mcu;bytes_per_blocklines[2] *= ystride_by_mcu;bytes_per_mcu[0] *= xstride_by_mcu/8;bytes_per_mcu[1] *= xstride_by_mcu/8;bytes_per_mcu[2] *= xstride_by_mcu/8;/* Just the decode the image by macroblock (size is 8x8, 8x16, or 16x16) */for (y=0; y < priv->height/ystride_by_mcu; y++){//trace("Decoding row %d\n", y);priv->plane[0] = priv->components[0] + (y * bytes_per_blocklines[0]);priv->plane[1] = priv->components[1] + (y * bytes_per_blocklines[1]);priv->plane[2] = priv->components[2] + (y * bytes_per_blocklines[2]);for (x=0; x < priv->width; x+=xstride_by_mcu){decode_MCU(priv);convert_to_pixfmt(priv);priv->plane[0] += bytes_per_mcu[0];priv->plane[1] += bytes_per_mcu[1];priv->plane[2] += bytes_per_mcu[2];if (priv->restarts_to_go>0){priv->restarts_to_go--;if (priv->restarts_to_go == 0){priv->stream -= (priv->nbits_in_reservoir/8);resync(priv);if (find_next_rst_marker(priv) < 0)return -1;}}}}
#if TRACEfprintf(p_trace,"Input file size: %d\n", priv->stream_length+2);fprintf(p_trace,"Input bytes actually read: %d\n", priv->stream - priv->stream_begin + 2);fflush(p_trace);
#endifreturn 0;
}

static void write_yuv——保存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);printf("write yuv begin!\n");//yuv都写入一个文件snprintf(temp, 1024, "%s.yuv", filename);F = fopen(temp, "wb");fwrite(components[0], width, height, F);   //写Yfwrite(components[1], width * height / 4, 1, F);fwrite(components[2], width * height / 4, 1, F);    //写UVfclose(F);   //关闭文件printf("write yuv done!\n");}

三、实验结果

打开out_yuv.yuv:

前后对比:

霍夫曼码表——直流系数DC

霍夫曼码表——交流系数AC

输出DC、AC图像并统计其概率分布 

更改tinyjpeg_decode:

int tinyjpeg_decode(struct jdec_private *priv, int pixfmt)
{unsigned int x, y, xstride_by_mcu, ystride_by_mcu;unsigned int bytes_per_blocklines[3], bytes_per_mcu[3];decode_MCU_fct decode_MCU;const decode_MCU_fct *decode_mcu_table;const convert_colorspace_fct *colorspace_array_conv;convert_colorspace_fct convert_to_pixfmt;unsigned char* DCbuf, * ACbuf;unsigned char* uvbuf = 128;int count = 0;if (setjmp(priv->jump_state))return -1;/* To keep gcc happy initialize some array */bytes_per_mcu[1] = 0;bytes_per_mcu[2] = 0;bytes_per_blocklines[1] = 0;bytes_per_blocklines[2] = 0;decode_mcu_table = decode_mcu_3comp_table;switch (pixfmt) {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;case TINYJPEG_FMT_RGB24:colorspace_array_conv = convert_colorspace_rgb24;if (priv->components[0] == NULL)priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3);bytes_per_blocklines[0] = priv->width * 3;bytes_per_mcu[0] = 3*8;break;case TINYJPEG_FMT_BGR24:colorspace_array_conv = convert_colorspace_bgr24;if (priv->components[0] == NULL)priv->components[0] = (uint8_t *)malloc(priv->width * priv->height * 3);bytes_per_blocklines[0] = priv->width * 3;bytes_per_mcu[0] = 3*8;break;case TINYJPEG_FMT_GREY:decode_mcu_table = decode_mcu_1comp_table;colorspace_array_conv = convert_colorspace_grey;if (priv->components[0] == NULL)priv->components[0] = (uint8_t *)malloc(priv->width * priv->height);bytes_per_blocklines[0] = priv->width;bytes_per_mcu[0] = 8;break;default:
#if TRACEfprintf(p_trace,"Bad pixel format\n");fflush(p_trace);
#endifreturn -1;}xstride_by_mcu = ystride_by_mcu = 8;if ((priv->component_infos[cY].Hfactor | priv->component_infos[cY].Vfactor) == 1) {decode_MCU = decode_mcu_table[0];convert_to_pixfmt = colorspace_array_conv[0];
#if TRACEfprintf(p_trace,"Use decode 1x1 sampling\n");fflush(p_trace);
#endif} else if (priv->component_infos[cY].Hfactor == 1) {decode_MCU = decode_mcu_table[1];convert_to_pixfmt = colorspace_array_conv[1];ystride_by_mcu = 16;
#if TRACEfprintf(p_trace,"Use decode 1x2 sampling (not supported)\n");fflush(p_trace);
#endif} else if (priv->component_infos[cY].Vfactor == 2) {decode_MCU = decode_mcu_table[3];convert_to_pixfmt = colorspace_array_conv[3];xstride_by_mcu = 16;ystride_by_mcu = 16;
#if TRACE fprintf(p_trace,"Use decode 2x2 sampling\n");fflush(p_trace);
#endif} else {decode_MCU = decode_mcu_table[2];convert_to_pixfmt = colorspace_array_conv[2];xstride_by_mcu = 16;
#if TRACEfprintf(p_trace,"Use decode 2x1 sampling\n");fflush(p_trace);
#endif}resync(priv);/* Don't forget to that block can be either 8 or 16 lines */bytes_per_blocklines[0] *= ystride_by_mcu;bytes_per_blocklines[1] *= ystride_by_mcu;bytes_per_blocklines[2] *= ystride_by_mcu;bytes_per_mcu[0] *= xstride_by_mcu/8;bytes_per_mcu[1] *= xstride_by_mcu/8;bytes_per_mcu[2] *= xstride_by_mcu/8;/* Just the decode the image by macroblock (size is 8x8, 8x16, or 16x16) */for (y=0; y < priv->height/ystride_by_mcu; y++){//trace("Decoding row %d\n", y);priv->plane[0] = priv->components[0] + (y * bytes_per_blocklines[0]);priv->plane[1] = priv->components[1] + (y * bytes_per_blocklines[1]);priv->plane[2] = priv->components[2] + (y * bytes_per_blocklines[2]);for (x=0; x < priv->width; x+=xstride_by_mcu){decode_MCU(priv);DCbuf = (unsigned char)((priv->component_infos->DCT[0] + 512.0) / 4 + 0.5);ACbuf = (unsigned char)(priv->component_infos->DCT[1] + 128);fwrite(&DCbuf, 1, 1, DCfile);fwrite(&ACbuf, 1, 1, ACfile);count++;convert_to_pixfmt(priv);priv->plane[0] += bytes_per_mcu[0];priv->plane[1] += bytes_per_mcu[1];priv->plane[2] += bytes_per_mcu[2];if (priv->restarts_to_go>0){priv->restarts_to_go--;if (priv->restarts_to_go == 0){priv->stream -= (priv->nbits_in_reservoir/8);resync(priv);if (find_next_rst_marker(priv) < 0)return -1;}}}}
#if TRACEfprintf(p_trace,"Input file size: %d\n", priv->stream_length+2);fprintf(p_trace,"Input bytes actually read: %d\n", priv->stream - priv->stream_begin + 2);fflush(p_trace);
#endiffor (int j = 0; j < count * 0.25 * 2; j++){fwrite(&uvbuf, sizeof(unsigned char), 1, DCfile);fwrite(&uvbuf, sizeof(unsigned char), 1, ACfile);}return 0;
}

(在打开YUVviewerPlus时注意:因为原图是1024×1024,所以输出的DC、DC图像的大小为128×128,都是4:2:0采样)

图像 概率分布

四、实验总结

理解了程序设计的整体框架,明白了JPEG编码的过程主要是采用量化、离散余弦变换(DCT)以及熵编码的联合编码方式,来去除数据本身的冗余信息和视觉冗余信息。

数据压缩作业:JPEG原理分析及JPEG解码器的调试相关推荐

  1. JPEG原理分析及JPEG解码器的解析

    文章目录 JPEG原理分析及JPEG解码器的调试 原理分析 JPEG编解码流程图 DC系数编码 AC系数编码 JPEG文件格式 Segment组织形式 JPEG 的 Segment Marker no ...

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

    文章目录 数据压缩实验(五) 一.JPEG原理分析 1.概述 优点 缺点 2.JPEG编解码原理 (1)彩色空间 (2)Level offset--零偏置电平下移 (3)8x8 DCT--离散余弦变换 ...

  3. 【数据压缩-实验5】JPEG原理分析及JPEG解码器的调试

    目录 JEPG原理 简述 优点 缺点 JPEG文件格式 常用标记码 编解码原理 编码原理 Level offset-零偏置 DCT变换 量化 DC系数差分编码 AC系数的之字形扫描+游程编码 解码原理 ...

  4. 【数据压缩】实验:JPEG原理分析及JPEG解码器的调试

    一.JPEG编解码原理 1.1 JPEG图像压缩标准基本介绍 JPEG是Joint Photographic Experts Group(联合图像专家小组)的缩写,文件后缀名为.jpg或.jpeg,是 ...

  5. JPEG原理分析及JPEG解码器调试

    JPEG格式简介 JPEG( Joint Photographic Experts Group)即联合图像专家组,是用于连续色调静态图像压缩的一种标准,文件后缀名为.jpg或.jpeg,是最常用的图像 ...

  6. 【数据压缩】实验五——JPEG原理分析及JPEG解码器的调试

    (一)实验目的 掌握JPEG编解码系统的基本原理.初步掌握复杂的数据压缩算法实现,并能根据理论分析需要实现所对应数据的输出. JPEG( Joint Photographic Experts Grou ...

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

    一.JPEG编码原理 1.将RGB转换为YUV空间.(相关性较小) 2.零偏置.(使最大的绝对值大的无符号数变为绝对值小的有符号数,减小数据平均的位数,例如0~255变为-128~127) 3.做8* ...

  8. 数据压缩实验五 JPEG原理分析JPEG解码器的调试

    一.实验原理 1.JPEG原理及编码流程   JPEG是常见的一种图像格式,由ISO与CCITT建立并开发,是一个国际数字图像压缩标准.JPEG文件的扩展名为.jpg或.jpeg,用有损方式去除冗余的 ...

  9. 实验五—JEPG 原理分析及 JPEG 解码器的调试

    文章目录 一.JPEG编码原理 1.Level Offset(水平偏移)能量的重新分配 2.DCT变换 3.Uniform scalar quantization 均匀标量量化 4.DC系数差分编码 ...

  10. JPEG 原理详细实例分析及其在嵌入式 Linux 中的应用

    http://www.ibm.com/developerworks/cn/linux/l-cn-jpeg/index.html 一.系统架构 本文以一个实际的产品为例,来说明 JPEG 在其中的应用. ...

最新文章

  1. js 跨页面的全局变量
  2. 精通JavaScript--07设计模式:行为型
  3. 一个object上拥有一个同步队列和一个等待队列
  4. 如何查看笔记本电脑配置参数_2020双十一(小白/学生)如何选购笔记本电脑?5000元预算哪款笔记本配置值得入手?...
  5. 【如何选】2019我的手机功能需求,千元机功能需求分析手机对比
  6. CLion 生成CMakeList文件和include文件不存在问题
  7. Please let us know in case of any issues
  8. vue获取当前月最后一天_10月的最后一天,有哪些不想谈恋爱适合发朋友圈的文案?...
  9. Neural Volumes Rendering(一)
  10. p=(1 r) 2c语言编程,菜鸟编程~!1.2
  11. koa2 mysql_koa2+vue+mysql 全栈开发记录
  12. mysql数据库索引回表_简述 MySQL 数据库的覆盖索引与回表
  13. 【洛谷P1169】[ZJOI2007]棋盘制作
  14. Mybatis安装配置使用
  15. img引用网络图片资源无法加载问题解决
  16. java 的初始化顺序
  17. 离散数学课程对应目录
  18. 个人所得税计算器(简易)
  19. 不在被虐中成长就在被虐中死亡
  20. 极智开发 | ubuntu 安装有线网卡驱动

热门文章

  1. 一个名牌大学毕业生心酸经历
  2. scp 及 ssh 命令出错解决
  3. 三星a60android9,三星A6060官方港版安卓9固件rom线刷包:TGY-A6060ZHU1ASH3
  4. JS继承--圣杯模式的详解
  5. mysql数据库加密方法l_使用透明数据库加密
  6. uni-app引用第三方插件(根据银行卡卡号查询银行类型和卡类型)
  7. AI如何修改画布尺寸? ai怎么设置画布大小_AI教程自学网
  8. Android 8.1 MTK平台 强制第三方 APP 横屏(微信、今日头条等)
  9. 怎么把证件照背景换成蓝色?一键更换照片背景
  10. 关于Jmeter线程数Ramp-Up.循环次数的理解和实验数据