实验5

您所在的位置:网站首页 照片压缩后是什么格式的 实验5

实验5

2024-07-14 08:16:07| 来源: 网络整理| 查看: 265

1 JPEG原理 1.1 JPEG简述

JPEG(Joint Photographic Experts Group)是联合图像专家组的英文缩写。 该组织从1986年正式开始制订静止数字图像的压缩编码标准,该标准于1992年正式通过,称为JPEG标准。 JPEG是第一个数字图像压缩的国际标准,它不仅适于静止图像的压缩,对 于电视图像序列的帧内压缩也常采用JPEG算法,因此JPEG是一个适用范围广泛的通用标准。

1.2 JPEG编码过程

在这里插入图片描述

1.2.0 RGB to YUV

为了减少各分量之间的相关性,减少数据的冗余,通常会把RGB颜色空间转换成YUV来进行各分量的编码。 在这里插入图片描述

1.2.1 Level Offset 零电平偏置下移

该步骤的作用是,图像内容平均亮度较高,将0电平移到中间,平均亮度降低, 便于DCT变换量化后直流的系数大大降低,也就降低了数据量。

将灰度级 2 n 2^n 2n的像素值,全部减去 2 n − 1 2^{n-1} 2n−1,数据形式由无符号数变为有符号数(补码),单极性数据变为双极性数据。

1.2.2 8x8 DCT变换

该步骤主要是用于去除图像数据之间的相关性,便于量化过程去除图像数据的空间冗余。

将图像分为8×8的像块;对于宽(高)不是8的整数倍的图像,使用图像边缘像素填充,以不改变频谱分布。然后对每一个子块进行DCT(Discrete Cosine Transform,离散余弦变换)。 DCT变换:在这里插入图片描述 其中,C是8x8的DCT变换二维核矩阵, f ( x , y ) f(x,y) f(x,y)是原始的数据。由于DCT变换是一个正交变换,故 C T = C − 1 \boldsymbol{C^T}=\boldsymbol{C^{-1}} CT=C−1。

变换核矩阵如下所示: 在这里插入图片描述 需要特别强调的是,DCT是一种无损变换,也无法对图像进行压缩,这样做的目的是在为下一步的量化做准备。

1.2.3 Uniform scalar quantization 均匀标量量化

量化器主要是利用人眼视觉特性设计而成的矩阵量化DCT系数,减少视觉冗余。 将DCT变换后的临时结果,除以各自量化步长并四舍五入后取整,得到量化系数。 JPEG系统分别规定了亮度分量和色度分量的量化表,色度分量相应的量化步长比亮度分量大。 在量化步骤中,JPEG采用了中平型(Midtread)的均匀量化器。关于中平型量化器的更多信息可以查看:作业:Lloyd - Max标量量化器条件的推导

量化是编码流程中唯一会引入误差也是唯一会带来压缩的步骤。Y、UV各一张表,共两张表。

1.2.4 直流:DPCM + VLC 可变长熵编码(采用Huffman)

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

系数的数值比较大相邻8×8图像块的DC系数值变化不大,冗余

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

DPCM是对于DC系数处理时所需要用到的预测编码方法。此方法在之前的实验中已经有详细说明。

对DPCM后算出的DIFF差值使用Huffman编码。 将其分成类别,类似于指数的Golomb编码(只不过Golomb是一元码+定长码),也就是类别ID使用规范哈夫曼编码,类内索引使用定长码(自然码)。

所以,DC系数会产生一张长度为16的Huffman码表。 在这里插入图片描述

1.2.5 交流:ZigZag Scan + Run Length Encoding+VLC

对于量化后的数据,我们将其分为两路进行处理。一路是AC通路,一路是DC通路。ZigZag Scan+RLE是用于AC通路的,这是因为AC分量出现较多的0。JPEG采用对0系数的游程长度编码。而对非0值,则要保存所需数和实际值。 在编码之前,需要把二维的变换系数矩阵转换为一维序列,由于量化之后右下角高频系数大部分为零,采用ZigZag Scan读取可以制造较长的零游程,提高编码效率。在扫描中,如果后续的系数全部为零,则用“EOB”表示块结束。 在这里插入图片描述 在扫描后,采用RLE进行编码。

1.2.5.1 RLE编码的过程: RLE编码。在JPEG编码中,RLE的含义就同其原有的意义略有不同。在JPEG编码中,假设RLE编码之后得到了一个(M,N)的数据对,其中M是两个非零AC系数之间连续的0的个数(即,行程长度),N是下一个非零的AC系数的值。采用这样的方式进行表示,是因为AC系数当中有大量的0,而采用Zigzag扫描也会使得AC系数中有很多连续的0的存在,这样就很适合RLE编码。

例:例如,现有一个字符串,如下所示: 57,45,0,0,0,0,23,0,-30,-8,0,0,1,000… 经过RLE之后,将呈现出以下的形式: (0,57) ; (0,45) ; (4,23) ; (1,-30) ; (0,-8) ; (2,1) ; (0,0) 注意,如果AC系数之间连续0的个数超过16,则用一个扩展字节(15,0)来表示16连续的0。

Huffman编码。 在进行了RLE后,仍然需要进行Huffman编码。对于任何一个RLE的数据对,如(0,57),都可以表示成(RRRR,SSSS)的形式。其中前面的0-16采用自然码RRRR,后面的SSSS则是与DC一致的Huffman分组的编码方式,存储索引。放到码流里的是其组内编码。 在这里插入图片描述

所以,最后总共有4张Huffman码表(亮度DC,亮度AC,色度DC,色度AC)。

那么,这些码表如何存储?源数据又放在哪里?针对这些未解之谜,接下来我们就分析JPEG的存储结构。

1.3 一个JPEG编码的示例

某个图象的一个8*8方块的亮度值: 在这里插入图片描述 Level Offset 后: 在这里插入图片描述

DCT变换后: 在这里插入图片描述 量化后: 在这里插入图片描述

其中,参照的量化表是: 在这里插入图片描述 随后,对于这个8*8方块的亮度量化后的数据分别进行AC和DC两路的编码。

2 JPEG文件格式的解析

由于后面需要导出码表等操作,势必需要掌握jpeg的存储格式。因此,我们针对实验所用的testrgb-2x2.jpg,作一个完整解析。

2.0 文件概览

在这里插入图片描述 JPEG以segment组成。每个segment都有一个名字,为其segment marker.我们逐一对每个marker进行分析。

整个文件以SOI开始,EOI结束。中间包含了APP0字段,两个DQT字段,一个SOF0字段,四个DHT字段,然后包含所有的ImageData数据。每个字段的作用都会在下面详细解释。

2.1 SOI(Start of Image)

图像以SOI(Start of Image)标志图像开始,内容为固定值FFD8。 在这里插入图片描述

2.2 APP0

接下来是APP0字段。APP0字段是应用程序保留标记0。 该字段以FFE0开启,后面包含信息: 在这里插入图片描述

2.3 DQT[0]

DQT就是DCT后的两张量化表,一张AC,一张DC。每张量化表都由FFDB字段开始。随后的4个字节说明了该字段的长度,然后存放的就是量化表。

量化表中的第一个字节被分成了高四位和低四位来用。高四位表示了该量化表的精度,0:8位;1:16位;低四位表示了量化表ID,取值范围为0~3;接下来是所有的表项,数量为(64×(精度+1))字节,里面都是量化的系数。量化表中的数据按照Z字形保存量化表内8x8的数据 。 在这里插入图片描述 该jpeg文件存放了0和1两张码表。

2.4 SOF0[0]

SOF0[0]为帧图像开始marker.以FFC0为开始标记 在这里插入图片描述 后两个字节标注数据长度;然后一个字节标注了每个颜色分量每个像素的位数(8),然后表明了行数和每行的采样点数,然后附上了三个components体信息。每个component都是一个颜色分量,内含颜色索引ID、Sample factor(高四位水平因子、低四位垂直因子)、和采用的量化表号。

2.5 DHT[0] (Define Huffman Table)

DHT是存放Huffman码表的地方。该Marker以FFC4作为开始标记。然后是字段长度,类型(AC/DC),索引(Index),位表(bit table),值表(value table)。

在这里插入图片描述 表的内容如课件所示。

在这里插入图片描述

彩蛋:课件上的数据与分析的图像是一张图像。

一共有4张DHT,对应AC/DC的Y/UV。

2.6 ImageData[0]-SOS

在大量的图像数据开始前,还有一个SOS字段。该字段表明了扫描开始,说明了数据是如何组织的。该字段以FFDA开始,然后表明了字段的长度,然后说明了颜色分量数,该与SOF字段中的数据应该是保持一致的。然后针对于每一个颜色分量信息,给出了每个分量的DC/AC使用的哈夫曼表编号。 在这里插入图片描述

这中间有一些课件未提及的数据?

在这里插入图片描述

然后是)谱选择开始、谱选择结束和谱选择固定值003F00。然后就是正式的图像数据了。

3 程序架构和结构体分析

从main入口开始,我们观察到程序设定了两个模式,一个跑分模式(benchmark_mode)和一个转换图像模式(convert_one_image)。由于我们这次不涉及到跑分模式,我们只对转换图像模式(convert_one_image)进行分析。 我们跳入函数convert_one_image,查看里面干了什么。首先,将程序加载到内存中然后就关闭;然后解码jpeg(这是最主要的工作);然后获取图像大小;然后获取每个通道的内存地址;拥有了获取的这些信息后,才可以以想要的输出方式存储。然后系统对于解码后的文件进行了写出,以用户选择的模式进行写出。

在该函数中,设置了这些变量:

FILE *fp; //打开的文件 unsigned int length_of_file; //文件长度 unsigned int width, height; //宽、高 unsigned char *buf; //存储的buffer struct jdec_private *jdec; //一个结构体指针 unsigned char *components[3]; //三个通道的指针

可以看到,后面的处理都是针对于jdec这个指针指向的jdec_private结构体进行处理的:

jdec = tinyjpeg_init(); //该函数用于初始化jdec结构体 tinyjpeg_parse_header(jdec, buf, length_of_file)//解析JPEG文件头 tinyjpeg_get_size(jdec, &width, &height); // 计算图像宽高 tinyjpeg_decode(jdec, output_format);// 解码实际数据 tinyjpeg_get_components(jdec, components); //获得每个通道的数据

所以,我们有必要对于jdec_private这个结构体进行一个分析。

3.1 结构体:jdec_private

jdec_private是每一个JPEG的码流中的一小块的结构体,包含了所有完整的内容的定义。

在jedc_private 中,定义了指向三个components的指针和三个components结构体(下面详述其意义);定义了图像的宽高;码流长度、始末指针;还有三张量化表(最终只用到两张,Y一张,UV一张);以及DC\AC各四张哈夫曼表(实际各用两张)。

3.2 结构体:components

components主要用于单个颜色通道的DCT变换后的值的存储;以及指明使用了哪个huffman_table和量化table。

3.3 结构体:huffman_table

huffman_table这个结构体主要用于存储所有的Huffman表。huffman表分为快速的查找表和慢速表。

4 解码过程更详细的解释

在tinyjpeg_parse_header中,解析了JPEG的文件头。在读完SOI后,调用parse_JFIF对于每个Marker进行解析。这个解析过程持续到遇到了sos Marker,也就是扫描行开始。

while (!sos_marker_found) 4.1 DQT解码

我们输入的JPEG文件需要先解码DQT,也就是量化表。在这一步里,构建起了对应的编号的量化表。通过信息的剥离,在parse_DQT()首先找到了对应的需要创建的是哪张量化表,然后就开始调用build_quantization_table开始创建了。

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 dqt_block_end = stream + be16_to_cpu(stream); stream += 2; /* Skip length */ while (stream /* 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 *qtable++ = ref_table[*zz++] * aanscalefactor[i] * aanscalefactor[j]; } } }

从中我们可以看出,量化表的建立传入了两个参数,一个是正式的要写入的量化表qtable,另一个是参考表(reftable),是从流中读取的数据。 对于这个数据,首先程序设定了一个比例因子, scalefactor[0] = 1 ;scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1…7,且存的是倒数,这是便于进行乘法运算的。 这里主要是因为,在编码的时候,进行了人眼视觉特性设计而成的矩阵量化DCT系数,解码的时候需要进行反操作。 且采用了zigzag扫描:

static const unsigned char zigzag[64] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63 };

因此,我们如果想要输出量化表,其实在这一步后面把ref_table表输出就可以了。码流里直接读出来的量化表是真正的量化表。后来计算出来的量化表,乘了比例因子,是为了辅助浮点dct ,idct 运算用的。如果是浮点运算,在编码端要乘比例因子,所以在解码端也要乘。具体的代码添加见下面。

4.2 SOF解码

接下来该JPEG头中是SOF,故解析了SOF,帧图像开始marker。这段解析较为简单,主要读出了图像的通道数量、图像的宽和高、每个通道的ID、水平垂直采样因子和使用的量化表等。

4.3 DHT解码

接下来就到了Huffman码表的解析。

length = be16_to_cpu(stream) - 2; // 表长(可能包含多张表) stream += 2; /* Skip length */ while (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 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); }

因此,想要完成这个任务非常容易,只需要将所有内容写入到一个文件中,输出一个.yuv文件即可。

static void write_yuv(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], width, height, F); fwrite(components[1], width*height/4, 1, F); fwrite(components[2], width*height/4, 1, F); fclose(F); }

将所有内容全部写入一个文件中,再次运行程序,已成功生成了我们所需的文件,打开进行查看: 在这里插入图片描述

至此,修改程序输出为YUV已完成。

6 TRACE输出所有的量化矩阵和所有的Huffman码表 6.1 TRACE:输出TXT

我们想要在程序执行的过程中获得一些调试信息时候,可以使用之前打开的TRACE文件进行输出。TRACE可以在代码中的任意地方进行条件编译。比如下面这段:

#if TRACE fprintf(p_trace,"< DQT marker\n"); fflush(p_trace); #endif

也就是说,当TRACE被设定为1,程序的if条件成立,就会执行这段宏内的条件编译内容,完成对于trace文件的写入。

至于TRACE和输出文件的设定,是在cpp头宏定义的:

#define TRACE 1 #define TRACEFILE "trace_jpeg.txt"//add by nxn

所以,我们想关闭TraceFile的输出,设置TRACE=0即可。

6.2 输出量化矩阵

在4.1小节我们已经说明,量化矩阵会在哪里产生,也就是build_quantization_table里。现在我们在该函数末尾加上一个TRACE,写上输出它的代码:

#if TRACE const unsigned char* anotherzz = zigzag; for (int i = 0; i fprintf(p_trace, "%-6d", ref_table[*anotherzz++]); if (j == 7) { fprintf(p_trace, "\n"); } } } #endif

输出结果: 在这里插入图片描述

6.3 输出Huffman码表

默认的代码中已经输出了所有的Huffman码表。在3.3中我们可以看出,AC和DC的Huffman码表都是在build_huffman_table函数中完成建立的。因此我们进入这两个函数,然后对他们进行查看,并输出码表。函数中已经写好:

#if TRACE fprintf(p_trace,"val=%2.2x code=%8.8x codesize=%2.2d\n", val, code, code_size); fflush(p_trace); #endif

在这里插入图片描述 我们可以在TRACE文件里看到所有码表内容。完整的输出可以在附录中查询到。

7 输出DC/AC图像

首先采用类似TRACEFILE文件的方式添加宏定义:

#define snprintf _snprintf//add by nxn #define TRACE 1 1//add by nxn #define TRACEFILE "trace_jpeg.txt"//add by nxn #define OUTPUTACDC 1 #define OUTPUTACFILE "output_ac.yuv" #define OUTPUTDCFILE "output_dc.yuv"

然后添加文件读写:

FILE *p_trace;//add by nxn FILE* output_ac; FILE* output_dc; # if OUTPUTACDC output_ac = fopen(OUTPUTACFILE, "wb"); output_dc = fopen(OUTPUTDCFILE, "wb"); # endif

图像的解码在解出jpeg头后的tinyjpeg_decode函数中完成。因此,我们跳转进该函数,首先添加需要开的buffer:

unsigned char* dcImgBuff; unsigned char* acImgBuff; unsigned char* uvBuff = 128; int count = 0;

在循环里:

for (x=0; x width; x+=xstride_by_mcu) { decode_MCU(priv); # if OUTPUTACDC 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, output_dc); fwrite(&acImgBuff, 1, 1, output_ac); count++; # endif 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) stream_length+2); fprintf(p_trace,"Input bytes actually read: %d\n", priv->stream - priv->stream_begin + 2); fflush(p_trace); #endif return 0; }

我们通过计数器,可以看到最后计数下来是4096个像素: 在这里插入图片描述

因此,在我们的打开图像时候应该选择64*64的4:2:0的图像。

可以看到效果: 在这里插入图片描述

8 概率统计

使用python对图像进行统计

import numpy as np import matplotlib.pyplot as plt import collections import pandas as pd from collections import Counter fraw = open("output_ac.yuv", "rb") frec = open("output_dc.yuv", "rb") raw = [] rec = [] i = 0 while i Unknown marker e0 > DQT marker 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 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 (????) precision:8 Component:1 factor:2x2 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=07 code=0000000e codesize=04 val=02 code=0000001e codesize=05 val=01 code=0000003e codesize=06 val=00 code=0000007e codesize=07 val=09 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=05 code=00000000 codesize=02 val=06 code=00000001 codesize=02 val=07 code=00000002 codesize=02 val=04 code=00000006 codesize=03 val=03 code=0000000e codesize=04 val=02 code=0000001e codesize=05 val=00 code=0000007c codesize=07 val=01 code=0000007d codesize=07 val=0a code=0000007e codesize=07 val=08 code=000000fe codesize=08 val=09 code=000001fe codesize=09 < DHT marker > DHT marker (length=43) Huffman table AC[1] length=26 val=00 code=00000000 codesize=02 val=01 code=00000002 codesize=03 val=03 code=00000003 codesize=03 val=04 code=00000004 codesize=03 val=05 code=00000005 codesize=03 val=02 code=0000000c codesize=04 val=21 code=0000000d codesize=04 val=31 code=0000000e codesize=04 val=61 code=0000001e codesize=05 val=11 code=0000003e codesize=06 val=41 code=000000fc codesize=08 val=06 code=000001fa codesize=09 val=12 code=000001fb codesize=09 val=13 code=000001fc codesize=09 val=15 code=000001fd codesize=09 val=14 code=000003fc codesize=10 val=22 code=000003fd codesize=10 val=51 code=000003fe codesize=10 val=07 code=000007fe codesize=11 val=32 code=00000ffe codesize=12 val=23 code=00003ffc codesize=14 val=71 code=00003ffd codesize=14 val=08 code=00007ffc codesize=15 val=16 code=00007ffd codesize=15 val=33 code=00007ffe codesize=15 val=81 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 2x2 sampling Input file size: 111269 Input bytes actually read: 111268 参考资料 现代电视原理课件《数据压缩》课件JPEG编解码原理及C++调试 by S.Z.Zheng实验提供的所有资料C条件编译非常感谢老师的讲解!


【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭