JPEG图像压缩

转载自:

https://blog.csdn.net/shelldon/article/details/54234433

www.thecodeway.com

http://www.ibm.com/developerworks/cn/linux/l-cn-jpeg/

一、图像分割

       JPEG算法的第一步,图像被分割成大小为8X8的小块,这些小块在整个压缩过程中都是单独被处理的。

 

                       

二、颜色空间转换RGB->YCbCr

       所谓“颜色空间”,是指表达颜色的数学模型,比如我们常见的“RGB”模型,就是把颜色分解成红绿蓝三种分量,这样一张图片就可以分解成三张灰度图,数学表达上,每一个8X8的图案,可以表达成三个8X8的矩阵,其中的数值的范围一般在[0,255]之间。

      

       不同的颜色模型各有不同的应用场景,例如RGB模型适合于像显示器这样的自发光图案,而在印刷行业,使用油墨打印,图案的颜色是通过在反射光线时产生的,通常使用CMYK模型,而在JPEG压缩算法中,需要把图案转换成为YCbCr模型,这里的Y表示亮度(Luminance),Cb和Cr分别表示绿色和红色的“色差值”。
       “色差”这个概念起源于电视行业,最早的电视都是黑白的,那时候传输电视信号只需要传输亮度信号,也就是Y信号即可,彩色电视出现之后,人们在Y信号之外增加了两条色差信号以传输颜色信息,这么做的目的是为了兼容黑白电视机,因为黑白电视只需要处理信号中的Y信号即可。
       根据三基色原理,人们发现红绿蓝三种颜色所贡献的亮度是不同的,绿色的“亮度”最大,蓝色最暗,设红色所贡献的亮度的份额为KRK_{R},蓝色贡献的份额为K_{R},那么亮度为

                                                                     (2.1)

       根据经验,K_{R}=0.299,K_{R}=0.114,那么

                                                                         (2.2)

       蓝色和红色的色差的定义如下

                                                                                                   (2.3)

                                                                                                   (2.4)

       最终可以得到RGB转换为YCbCr的数学公式为

                                                            (2.5)

       YCbCr模型广泛应用在图片和视频的压缩传输中。有损压缩首先要做的事情就是“把重要的信息和不重要的信息分开”,YCbCr恰好能做到这一点。对于人眼来说,图像中明暗的变化更容易被感知到,这是由于人眼的构造引起的。视网膜上有两种感光细胞,能够感知亮度变化的视杆细胞,以及能够感知颜色的视锥细胞,由于视杆细胞在数量上远大于视锥细胞,所以我们更容易感知到明暗细节。比如说下面这张图。

        

        Y Cb Cr

       可以明显看到,亮度图的细节更加丰富。JPEG把图像转换为YCbCr之后,就可以针对数据得重要程度的不同做不同的处理。这就是为什么JPEG使用这种颜色空间的原因。

三、离散余弦变换

       离散余弦变换(Discrete cosine transform,简称DCT),离散余弦变换属于傅里叶变换的另外一种形式。

       三角函数的确无法表达出尖角形状的函数,不过只要三角函数足够多,可以无限逼近最终结果。比如下面这张动图,就动态描述了一个矩形方波,是如何做傅里叶分析的。

       当我们要处理的不再是函数,而是一堆离散的数据时,并且这些数据是对称的话,那么傅里叶变化出来的函数只含有余弦项,这种变换称为离散余弦变换。举个例子,有一组一维数据[x0,x1,x2,…,xn-1],那么可以通过DCT变换得到n个变换级数Fi

                                                                   (3.1)

       此时原始数据Xi可以通过离散余弦变换变化的逆变换(IDCT)表达出来

                                                        (3.2)

       也就是说,经过DCT变换,可以把一个数组分解成数个数组的和,如果我们数组视为一个一维矩阵,那么可以把结果看做是一系列矩阵的和

     (3.3)

       举个例子,我们有一个长度为8的数字,内容为50,55,67,80,-10,-5,20,30,经过DCT转换,得到8个级数为287.0, 106.3, 14.2, -110.8, 9.2, 65.7, -8.2, -43.9,根据公式3.3把这个数组转换为8个新的数组的和,如果我们使用图像来表达的话,就可以发现DCT转换的有趣之处了。

[50, 55, 67, 80, -10, -5, 20, 30]

      

               数组0                             数组1                            数组2                            数组3

      

               数组4                             数组5                            数组6                            数组7

       奥妙之处在于,经过DCT,数据中隐藏的规律被发掘了出来,杂乱的数据被转换成几个工整变化的数据。DCT转换后的数组中第一个是一个直线数据,因此又被称为“直流数据”,简称DC,后面的数据被称为“交流数据”,简称AC,这个称呼起源于信号分析中的术语。
       在JPEG压缩过程中,经过颜色空间的转换,每一个8X8的图像块,在数据上表现为3个8X8的矩阵,紧接着我们对这三个矩阵做一个二维的DCT转换,二维的DCT转换公式为

                 (3.4)

       DCT的威力究竟有多大,我们可以做一个实际的测试,比如一个所有数值都一样的矩阵,经过DCT转换后,将所有级数组合成一个新的矩阵

       可以看到,经过DCT转换,矩阵的“能量”被全部集中在左上角上的直流分量F(0,0)上,其他位置都变成了0。
       在实际的JPEG压缩过程中,由于图像本身的连贯性,一个8X8的图像中的数值一般不会出现大的跳跃,经过DCT转换会有类似的效果,左上角的直流分量保存了一个大的数值,其他分量都接近于0,我们以Lenna左上角第一块图像的Y分量为例,经过变换的矩阵为

       可以看到,数据经过DCT变化后,被明显分成了直流分量和交流分量两部分,为后面的进一步压缩起到了充分的铺垫作用,可以说是整个JPEG中最重要的一步,后面我们会介绍数据量化。

四、数据量化

       经过上一节介绍的离散余弦变换,图像数据虽然已经面目全非,但仍然是处于“可逆”的状态,也就是说我们还没有进入“有损”的那一步。这次我们来玩真的,看一下数据中的细节是如何被滤去的。先来考察一下要对付的问题是什么,经过颜色空间转换和离散余弦变换,每一个8X8的图像块都变成了三个8X8的浮点数矩阵,分别表示Y,Cr,Cb数据,比如以其中某个亮度数据矩阵举例,它的数据如下

       我们的问题是,在可以损失一部分精度的情况下,如何用更少的空间存储这些浮点数?答案是使用量子化(Quantization),简称量化。“量子”这个概念来自于物理学,意思是说连续的能量可以看做是一个个单元体的组合,看起来高端大气,其实很简单,比如游戏中在处理角色面朝方向时,一般并不是使用0到2π这样的浮点数,而是把方向分成16个区间,用0到16这样的整数来表示,这样只用4个bit就足够了。JPEG提供的量子化算法如下:

                                                                                              (4.1)

       其中G是我们需要处理的图像矩阵,Q称作量化系数矩阵(Quantization matrices),JPEG算法提供了两张标准的量化系数矩阵,分别用于处理亮度数据Y和色差数据Cr以及Cb。

          标准亮度量化表         标准色差量化表

       其中round函数是取整函数,但考虑到了四舍五入。

       比如上面数据,以左上角的-415.38为例,对应的量子化系数是16,那么round(-415.38/16)=round(-25.96125)=-26。最终得到的量子化后的结果为

       可以看到,一大部分数据变成了0,这非常有利于后面的压缩存储。这两张神奇的量化表也是有讲究的,还记得我们在第一节中所讲的有损压缩的基本原理吗,有损压缩就是把数据中重要的数据和不重要的数据分开,然后分别处理。DCT系数矩阵中的不同位置的值代表了图像数据中不同频率的分量,这两张表中的数据时人们根据人眼对不不同频率的敏感程度的差别所积累下的经验制定的,一般来说人眼对于低频的分量必高频分量更加敏感,所以两张量化系数矩阵左上角的数值明显小于右下角区域。在实际的压缩过程中,还可以根据需要在这些系数的基础上再乘以一个系数,以使更多或更少的数据变成0,我们平时使用的图像处理软件在省城jpg文件时,在控制压缩质量的时候,就是控制的这个系数。
       在进入下一节之前,矩阵的量化还有最后一步要做,就是把量化后的二维矩阵转变成一个一维数组,以方便后面的霍夫曼压缩,但在做这个顺序转换时,需要按照一个特定的取值顺序。

       这么做的目的只有一个,就是尽可能把0放在一起,由于0大部分集中在右下角,所以才去这种由左上角到右下角的顺序,经过这种顺序变换,最终矩阵变成一个整数数组

       -26,-3,0,-3,-2,-6,2,-4,1,-3,0,1,5,,1,2,-1,1,-1,2,0,0,0,0,0,-1,-1,0,0,0,0,…,0,0

       后面的工作就是对这个数组进行再一次的哈夫曼压缩,已得到最终的压缩数据。

五、哈弗曼编码

       JPEG压缩的最后一步是对数据进行哈弗曼编码(Huffman coding),哈弗曼几乎是所有压缩算法的基础,它的基本原理是根据数据中元素的使用频率,调整元素的编码长度,以得到更高的压缩比。
       举个例子,比如下面这段数据

       “AABCBABBCDBBDDBAABDBBDABBBBDDEDBD”

       这段数据里面包含了33个字符,每种字符出现的次数统计如下

字符ABCDE
次数615291

       如果我们用我们常见的定长编码,每个字符都是3个bit。

字符ABCDE
编码001010011100101

       那么这段文字共需要3*33 = 99个bit来保存,但如果我们根据字符出现的概率,使用如下的编码

字符ABCDE
编码11001110101111

       那么这段文字共需要3*6 + 1*15 + 4*2 + 2*9 + 4*1 = 63个bit来保存,压缩比为63%,哈弗曼编码一般都是使用二叉树来生成的,这样得到的编码符合前缀规则,也就是较短的编码不能够是较长编码的前缀,比如上面这个编码,就是由下面的这颗二叉树生成的。

       我们回到JPEG压缩上,回顾上一节的内容,经过数据量化,我们现在要处理的数据是一串一维数组,举例如下:

①原始数据

35,7,0,0,0,-6,-2,0,0,-9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,…,0

       在实际的压缩过程中,数据中的0出现的概率非常高,所以首先要做的事情,是对其中的0进行处理,把数据中的非零的数据,以及数据前面0的个数作为一个处理单元。

①原始数据

35,7,0,0,0,-6,-2,0,0,-9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,…,0

②RLE编码3570,0,0,-6-20,0,-90,0,…,0,80,0,…,0

       如果其中某个单元的0的个数超过16,则需要分成每16个一组,如果最后一个单元全都是0,则使用特殊字符“EOB”表示,EOB意思就是“后面的数据全都是0”,

①原始数据

35,7,0,0,0,-6,-2,0,0,-9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,…,0

②RLE编码3570,0,0,-6-20,0,-90,0,…,0,80,0,…,0
3570,0,0,-6-20,0,-90,0,…,00,0,80,0,…,0
(0,35)(0,7)(3,-6)(0,-2)(2,-9)(15,0)(2,8)EOB

       * RLE - Run-Length Encoding,即行程编码,对于连续重复出现的字符有很好的压缩率。编码后为(字符出现次数,字符)。
       * JPEG 中实际使用的是(字符出现次数 - 1,字符)

       其中(15,0)表示16个0,接下来我们要处理的是括号里右面的数字,这个数字的取值范围在-2047~2047之间,JPEG提供了一张标准的码表用于对这些数字编码:

ValueSizeBits
00-
-11101
-3,-22,3200,0110,11
-7,-6,-5,-44,5,6,73000,001,010,011100,101,110,111
-15,…,-88,…,1540000,…,01111000,…,1111
-31,…,-1616,…,3150 0000,…,0 11111 0000,…,1 1111
-63,…,-3232,…,63600 0000,……,11 1111
-127,…,-6464,…,1277000 0000,……,111 1111
-255,…,-128128,…,25580000 0000,……,1111 1111
-511,…,-256256,…,51190 0000 0000,……,1 1111 1111
-1023,…,-512512,…,10231000 0000 0000,……,11 1111 1111
-2047,…,-10241024,…,204711000 0000 0000,……,111 1111 1111

       举例来说,第一个单元中的“35”这个数字,在表中的位置是长度为6的那组,所对应的bit码是“100011”,而“-6”的编码是”001″,由于这种编码附带长度信息,所以我们的数据变成了如下的格式。

①原始数据

35,7,0,0,0,-6,-2,0,0,-9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,…,0

②RLE编码3570,0,0,-6-20,0,-90,0,…,0,80,0,…,0
3570,0,0,-6-20,0,-90,0,…,00,0,80,0,…,0
(0,35)(0,7)(3,-6)(0,-2)(2,-9)(15,0)(2,8)EOB
③BIT编码(0,6, 100011)(0,3, 111)(3,3, 001)(0,2, 01)(2,4, 0110)(15,-)(2,4, 1000)EOB

       括号中前两个数字分都在0~15之间,所以这两个数可以合并成一个byte,高四位是前面0的个数,后四位是后面数字的位数。

①原始数据

35,7,0,0,0,-6,-2,0,0,-9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,…,0

②RLE编码3570,0,0,-6-20,0,-90,0,…,0,80,0,…,0
3570,0,0,-6-20,0,-90,0,…,00,0,80,0,…,0
(0,35)(0,7)(3,-6)(0,-2)(2,-9)(15,0)(2,8)EOB
③BIT编码(0,6, 100011)(0,3, 111)(3,3, 001)(0,2, 01)(2,4, 0110)(15,-)(2,4, 1000)EOB
(0x6,100011)(0x3,111)(0x33,001)(0x2,01)(0x24,0110)(0xF0,-)(0x24,1000)EOB

       对于括号前面的数字的编码,就要使用到我们提到的哈弗曼编码了,比如下面这张表,就是一张针对数据中的第一个单元,也就是直流(DC)部分的哈弗曼表,由于直流部分没有前置的0,所以取值范围在0~15之间。

LengthValueBits

3 bits

 

 

 

 

 

 

04
05
03
02
06
01
00 (EOB)
000
001
010
011
100
101
110
4 bits071110
5 bits081111 0
6 bits091111 10
7 bits0A1111 110
8 bits0B1111 1110

       举例来说,示例中的DC部分的数据是0x06,对应的二进制编码是“100”,而对于后面的交流部分,取值范围在0~255之间,所以对应的哈弗曼表会更大一些

LengthValueBits

2 bits

 

01
02
00
01
3 bits03100

4 bits

 

 

00 (EOB)
04
11
1010
1011
1100

5 bits

 

 

05
12
21
1101 0
1101 1
1110 0

6 bits

 

31
41
1110 10
1110 11

12 bits

 

 

 

24
33
62
72
1111 1111 0100
1111 1111 0101
1111 1111 0110
1111 1111 0111
15 bits821111 1111 1000 000

16 bits

 

 

09

FA
1111 1111 1000 0010

1111 1111 1111 1110

       这样经过哈弗曼编码,并且序列化后,最终数据成为如下形式

①原始数据

35,7,0,0,0,-6,-2,0,0,-9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,…,0

②RLE编码3570,0,0,-6-20,0,-90,0,…,0,80,0,…,0
3570,0,0,-6-20,0,-90,0,…,00,0,80,0,…,0
(0,35)(0,7)(3,-6)(0,-2)(2,-9)(15,0)(2,8)EOB
③BIT编码(0,6, 100011)(0,3, 111)(3,3, 001)(0,2, 01)(2,4, 0110)(15,-)(2,4, 1000)EOB
(0x6,100011)(0x3,111)(0x33,001)(0x2,01)(0x24,0110)0xF0(0x24,1000)EOB
④哈弗曼编码1001000111001111111 1111 010100101011111 1111 010001101111 1111 0011111 1111 010010001010
⑤序列化

100100011100111111111110101001010111111111010001101111111100111111111010010001010

91 CF FE A5 7F D1 BF CF FA 45

       最终我们使用了10个字节的空间保存了原本长度为64的数组,至此JPEG的主要压缩算法结束,这些数据就是保存在jpg文件中的最终数据。

 

一个JPEG图片实例分析

       我们可以打开 JPEG 文件查看里面的内容,即可看到上面的各个标记段:

       在头部有 FFD8 ,表示图像的开始;结束部分有 FFD9 ,表示图像的结束。在中间有两个量化表 DQT 对应的标记 FFDB ;还有图像大小信息对应的 FFC0;再后面有四个 Haffman 表对应的 FFC4 ;然后是图像数据段标记 FFDA。

       一般一个 JPEG 文件里会有 2 类 Haffman 表:一个用于 DC,一个用于 AC ,也即实际有 4个表,亮度的 DC,AC 两个,色度的 DC,AC 两个。

1、图片的识别信息

       上面的内容,在标记 FFE0 后,即为长度16。然后是5字节的 JFIF 标识符号,说明这是一个 JPEG 压缩的文件。然后是主/次版本号码。下一个为 XY 像素的单位,这里为1,表示单位为点数/英寸。然后是 XY 方向的像素密度,这里是 96DPI,最后是缩略图有关信息,这里为0。

2、量化表的实例

       上面这个内容,FFDB 标记后的长度值为67,接下来的是 QT 信息,占一个字节;这里是0,表示这个 QT 表编号为0,并且精度是8bit。然后后面就是64个8x8的 QT 表的各个 item 了。也即第一个 DQT 量化表的内容表示为十进制是:

JPEG亮度量化表

       第二个量化表的内容为:

JPEG 色度量化表

3、图像信息段

       上面这个内容,FFC0 标记后即是长度,为17;然后是一个字节的数据精度,通常是为8,代表样本位数。接下来是图片的高度,占两字节,这里即为8,然后是图片的宽度,也为8,这也就是我们定义的8x8的内容。然后是 component 的个数,这里是3,表示 YUV。接下来是三组数据,每组数据里,第一个是 component ID,第二个是采样系数,这里 Y 的采样系数为22,说明垂直是2,水平是2。再后面就是量化表的编号了。

4、Haffman 表的实例

       上面这个内容,FFC4 标记后的内容为数据长度,再接着的1字节为 Huffman Table 的信息,低4位是 HT ID 号,第5位是 HT 表类型标记,再高三位是为0。

信息HT类型HT ID号
00DC0
10AC0
01DC1
11AC1

       即前两个表为Y亮度分量的 DC/AC 表,后两个为 UV 色度分量的 DC/AC 表。

       以第一个表为例,因为长度只有 31,那么 00 后面的 16 字节,即绿色部分:

       组号为 1 的组中,代码有 0 个;组号为 2 的,代码有 1 个;组号为 3 的代码有 5 个;组号为 4/5/6/7/8/9 的代码各 1 个。

总共 12 个。

       再看后续的数据:00 01 02 03 04 05 06 07 08 09 0A 0B,即对应:

       其他未出现的组号,对应的数据未使用到。也就是说前面提到过的范式 Huffman 编码里,目前只使用部分数据即可,原因是这个 8x8 的图像数据很小。

       第二个 DHT 表就更复杂些了,长度有 181。

5、图像数据段

       这里 SOS 段,长度为 12,后面所含有的 component 数量为 3 个,也即 YUV。然后后面是各 component 的编号,及对应所使用的 Huffman 表的 ID 是多少。在这个段的后面就是所有压缩后的数据。直到结束的问题,即 FFD9,EOI(End Of Image)。

  • 13
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
#ifndef JPEGDECODE_H #define JPEGDECODE_H #include "global.h" #include "globalextern.h" typedef unsigned char BYTE; struct ImageComponentData { double value[3]; }; class MBitReader { public: BYTE* Data; int m_currentData; int m_currentDataIndex; int m_currentBitPosition; MBitReader(BYTE* data,int currentDataIndex) { Data=data; m_currentBitPosition=8; m_currentDataIndex=currentDataIndex; m_currentData=Data[m_currentDataIndex]; } public: int ReadNextBit() { if (m_currentBitPosition-1> m_currentBitPosition) & 0x01; } }; class MJpegDecode { public: struct _JFIFAPPOInfo { BYTE APP0[2]; /* 02h Application Use Marker */ BYTE Length[2]; /* 04h Length of APP0 Field */ BYTE Identifier[5]; /* 06h "JFIF" (zero terminated) Id String */ BYTE Version[2]; /* 0Bh JFIF Format Revision */ BYTE Units; /* 0Dh Units used for Resolution */ BYTE Xdensity[2]; /* 0Eh Horizontal Resolution */ BYTE Ydensity[2]; /* 10h Vertical Resolution */ BYTE XThumbnail; /* 12h Thumbnail Horizontal Pixel Count */ BYTE YThumbnail; /* 13h Thumbnail Vertical Pixel Count */ } JFIFAPPOINFO; struct _JFIFDQTInfo { BYTE DQT[2]; // 14h 量化表段标记 BYTE Length[2]; // 16h 量化表段长度 BYTE Identifier; // 18h 量化表ID BYTE QTData[64]; // 19h 量化表数据 } JFIFDQTINFO[2]; struct _JFIFSOFOInfo { BYTE SOFO[2]; // 9Eh 帧开始段标记 BYTE Length[2]; // A0h 帧开始段长度 BYTE BitCount; // A2h 样本精度bit位数 BYTE Height[2]; // A5h 图像像素宽度 BYTE Width[2]; // A3h 图像像素高度 BYTE ComponentsCount; // A7h 图像组件计数 BYTE YIdentifier; // A8h 亮度Y的ID号 BYTE YHVSamplingCoefficient; // A9h 亮度Y垂直和水平采样系数 BYTE YUsedDQTIdentifier; // AAh 亮度Y使用的量化表ID号 BYTE CbIdentifier; // ABh 色度Cb的ID号 BYTE CbHVSamplingCoefficient; // ACh 色度Cb垂直和水平采样系数 BYTE CbUsedDQTIdentifier; // ADh 色度Cb使用的量化表ID号 BYTE CrIdentifier; // AEh 色度Cr的ID号 BYTE CrHVSamplingCoefficient; // AFh 色度Cr垂直和水平采样系数 BYTE CrUsedDQTIdentifier; // B0h 色度Cr使用的量化表ID号 } JFIFSOFOINFO; struct _JFIFDRIInfo { BYTE DRI[2]; BYTE Length[2]; BYTE NMCUReset[2]; //每n个MCU块就有一个 RSTn 标记. } JFIFDRIINFO; struct _JFIFDHTInfo { BYTE DHT[2]; // B1h 哈夫曼表定义段标记 BYTE Length[2]; // B3h 哈夫曼表段长度 BYTE HTIdentifier; // B5h 哈夫曼表号 BYTE NBitsSymbolsCount[16]; // B6h (符号的二进制位长度为n)的符号个数 BYTE SymbolsTable[256]; // C6h 按递增次序代码长度排列的符号表 } JFIFDHTINFO[2][2]; struct _JFIFSOSInfo { BYTE SOS[2]; // 261h 扫描开始段标记 BYTE Length[2]; // 263h 扫描开始段长度 BYTE ComponentsCount; // 265h 扫描行内组件的数量 BYTE YIdentifier; // 266h 亮度Y的ID号 BYTE YHTTableID; // 267h 亮度Y使用的哈夫曼表ID号 BYTE CbIdentifier; // 268h 色度Cb的ID号 BYTE CbHTTableID; // 269h 色度Cb使用的哈夫曼表ID号 BYTE CrIdentifier; // 26Ah 色度Cr的ID号 BYTE CrHTTableID; // 26Bh 色度Cr使用的哈夫曼表ID号 BYTE Reserved[3]; // 26Ch 3个未知保留字节 } JFIFSOSINFO; private: struct HuffmanTable { int CodeOfFirstNLengthSymbol[17]; //长度为N的第一个码字的整数值 int NLengthToSymbolsTableIndex[16]; //查表得到第一个长度为N的符号位于符号表的索引 } HUFFMANTABLE[2][2]; public: int ReadJFIFInfo(const BYTE* const jfifData,int jfifDataSize); void DecodeData(int mcuStartIndex,BYTE* jfifData,int jfifDataSize,ImageComponentData*& targetBitmapData); void SetHuffmanTable(); void DecodeOneDUDC(MBitReader* myBitReader,double* DU,double& lastDC,int index1,int index2); void DecodeOneDUAC(MBitReader* myBitReader,double* DU,int index1,int index2); void DecodeOneMCU(MBitReader* myBitReader,int mcuXn,int mcuYn,int mcuWidth,int mcuHeight,double *DU,ImageComponentData* targetImage); void InverseQuantization(double* du,BYTE* quantizationTable); void InverseZigzag(double* sourceDU,double* targetDU); void IDCT(double* sourceDU,double* targetDU); void YCbCrToRGB(ImageComponentData* sourceImage,ImageComponentData* targetImage); public: int imageHeight; int imageWidth; int alignedImageWidth; int alignedImageHeight; HuffmanTable* HT; double DC[3]; int HSamplingCoefficient[3]; int VSamplingCoefficient[3]; int DQTID[3]; int nMCUReset; }; #endif // JPEGDECODE_H

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值