解读JPEG文件

 

解读JPEG文件

告诉您:JPEG这种压缩标准是如何进行计算的,以及动手压缩图片

 

我们知道,GIF压缩标是基于把图片转化成256色(或以下)位图的基础上进行压缩的,经过压缩的图片,颜色信息大量丢失。那么,有没有在真色彩(24位)下的图片压缩呢?JPEG压缩就能压缩24位图片。JPEG是世界联合图像专家组的缩写,JPEG压缩标准也是该组织制定的,版本有1.1 1.2两个。

 

JPEG压缩标准:

   RGB       分割         FDCT      RLE      哈夫曼        哈夫曼     写入

转化YUV   8*8大小块      量化      0编码     DC编码       AC编码   文件

 

                          量化表               哈夫曼树

          

                       标准量化表             标准哈夫曼表

 

 

 

 

 

 

 

 

 

 

 


上面表描叙了JPEG的压缩过程:

第一步  它把24位图像的RGB流转化成YUV流(分别代表亮度,色差,色差。也有称YCrCb的):

Y=  0.299 * R + 0.587 * G + 0.114 * B

U=  -0.1687 *R - 0.3313 * G + 0.5 * B +128

V=  0.5 * R - 0.4187 * G - 0.0813 * B +128

亮度,色差这些概念不是和电视机上的概念相似吗?是的,只是电影文件用的是MPEG压缩(动态图像压缩),而JPEG是静态图像压缩。

    第二步  由于人的眼睛对亮度的反应比较敏感,对色差的反应不敏感,因此,对亮度的扫描是逐点逐行、正向进行的(从左上角逐行开始),对色差的扫描可以逐点逐行进行,可以隔点隔行进行(为了提高压缩比),也可以隔点不隔行,或隔行不隔点等形式。由于JPEG是以8*8为单元进行处理图像的,所以就有以下几种情况

 

                                 16                  16                    16

                8

图像大小:

8             16                 16                    16

UV扫描:逐点逐行      隔点隔行          隔点不隔行            隔行不隔点

 

Y UV  Y1        Y4           Y4                Y4    

的数量:UV:各1   UV:各1        UV:各2            UV:各2

                                      (上一个下一个)       (左一个右一个)  

压缩格式:111      411            422               422

 

 

 

 

 

 

 

 

 

 

 

 

 

 


这就要求图像的宽和高必须是8 16的倍数,如果不是要在RGB流中,在相对于图像的右、下部分,添加颜色值为0的像素,直到816的倍数。

因为这样,JPEG就有上叙的4种不同的压缩格式。

第三步    8*8中的64个值进行FDCT处理,并Z字形重新排列。

64个数值进行FDCT(离散余弦变换)之前,每个值被减去128,变成-128~127之间的数。

FDCT的作用就是把这64个按空间排列的数转化成用频率表示。FDCT的计算量是非常大的,它是整个JPEG压缩过程中最耗时的环节。现在网上有大虾们提供的许多优化算法,其中(AA&N运算)是较普遍的一种,我还无法对各种优化算法进行一一的比较,但是,有一点是肯定的,FDCT算法将直接影响着压缩程序的运行速度,以及计算后所产生的误差值的大小(即图像被解码显示后,发生了失真。失真是在所难免的,关键是失真要小)。

我们平常所叫的软件的优化,FDCT就是JPEG压缩程序优化的切入点,如何在运算速度和产生误差方面进行改进(或是在两者之间寻找平衡点),许许多多的有志之士正乐此不疲地钻研着。而我在后面压缩程序举例中用到的FDCT算法,不管在速度还是误差方面都有些不如意,如有找到更好的,我会补上的。

 

Z字形排列:对这64个数字进行下表列出的顺序进行重新排列,目的是重新排列后的数字,使它们的位置与距离左上角的远近程度成正比(如:220距离左上角近,排在前面)以备量化时用

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

 

 

 

 

 

 

 

 

 

 


第四步    量化

JPEG压缩中有一个影响图像质量和压缩图像文件大小的系数,那就是“品质”,取值范围在0~100之间,标准量化表根据这个系数生成文件的量化表,再由文件量化表生成一个8*8空间的频率变化表,使左上角形成低频区,右下角形成高频区,然后用用排列后的64个数值乘以相应的频率,这样,64个数值中后面排在后面的数就会大量的变成0。而这些0在后面的将被压缩,JPEG的真正意义上的压缩就是从这里开始的。

我们可以换句话说,品质决定着频率,频率决定着0的多少,从而影响着压缩文件的大小,也影响着压缩图像的质量。JPEG压缩造成部分颜色信息的丢失也是在这个过程中。

在这64个数值(0~63)中,由于0处在最低频处,在乘以频率后值的消耗最小,我们把这个值看成是所有64个数值的平均值,称它为DC,也就是直流电平(电流);其余的63个量化后的数值,称它们为AC,也就是交流电平。

第五步    RLE编码

由于经量化后,64个数值中后面排在后面的数出现大量的0,接下来就对0进行RLE编码。例如:64个数值的后面63个数字(第1 个数是DC,另外分开编码)是

30- 400100   ……

经编码后为(023)(13- 4)(211)(EOB

023)说明,0表示3的前面有002表示3写成二进制有2位,即11

13- 4 )说明,1表示 – 4 的前面有1 03表示4的二进制有3

EOB)代表后面的所有0,如果不是以0结束就不用(EOB

而上面每组中的三个数又被整合成两个Byte,(0~255之间的数字)。前面的两个被写成1 个,第一个(表示0 的个数的值(也称0行程))写在高4位,每二个(表示非0的数(也称振幅)的二进制的长度)写在低4位。

如(023)中,02经过运算(0*16+2)被写成2 ,得到23两个Byte

正因为这样,也带来了许多的问题。

首先,表示0行程的值只有4位(取值范围0~15),如果AC中(排在后面的63个)连续0的个数超过15个,就要写入一组(1500),代表0的前面有150,加上本身一共有160,然后继续RLE编码。

其次,振幅可能是负值,而Byte只能是正数,怎么表示负值呢?原来,振幅是负值时,要把它转化为反码。反码是这样计算的,如 – 4 先取绝对值4,然后转化为二进制为100,反码就是二进制同位上取反值,变成011,再转化为Byte就是3(但二进制长度仍为3位)。

如(13- 4)转化为19 – 4 是错的,应为193

第六步    哈夫曼编码(Huffman

上面提到的REL纺码都是从64个中的第2 个开始的,第一个数是DC,它并不直接保存,而是保存它与前面方块(64个数)的DC之间的差异值。如:前一个方块的DC5,本方块的DC40,那么就保存成35。实际被保存成(635)前面的6 表示35转化成二进制共有6 位(100011)。

JPEG文件中对数值并不是一个Byte接一个Byte地直接保存。我们回看前面生成的每组中的两个数(如DC的(65)、AC23)或(193)等),第一个数中都有表示第二个数值的二进制长度的成分,JPEG先把第一个数(包含第二个数值的二进制长度信息)经过哈夫曼编码,先保存下来;再保存第二个数值时,就按数值实际的二进制长度进行保存。哈夫曼编码的作用我们可以这样来理解,它把0~255Byte的取值范围)这256个定长的数字(二进制都是8位),按照它们最容易出现的概率排列,用(二进制)2~16位的不同的变长数值表示,实际的取值为0~65535,这样来达到压缩文件的目的。

我们接着上面举的例子来说明,写入文件的数依次是:

6  35       2  3)(19  3)(33  1)(EOB

DC差异值          ACREL编码

拿每组数字的前面的数字去查相对应的哈夫曼表,获得表值,写入文件。再把后面的数字按实际位数写入文件。注意,我这里讲的是相应的哈夫曼表,一个JPEG文件,一般要建立四个哈夫曼表(在后面的文件组成部分会讲到),分别是DCYHT DCUVHT ACYHT ACUVHT。因为DCAC要分开,YUV也要分开。

我们假设上面举的例子中的流是Y(亮度)流为例,6为索引查DCYHT得到表值14(长4位);2 ACYHT得到表值1(长2位);19 ACYHT得到表值121(长7位);33 ACYHT得到表值28(长5位);EOBACYHT表中第一个(索引为0)的值0(长度2)。这样我们就得到了写入文件的数字流为:

14         35    1    3    121   3    28    1    0

4bit  6bit  2bit  3bit   7bit  2bit  5bit  1bit  2bit 

(第4个和第6个都是3,但第4 个是011(表示 - 4);第6个是11(表示3))

转化成二进制为:(共 32 位(Bits))

1110 100011 01 011 1111001 11 11100 1 00

8 位化成一个Byte,就成了:

232  215  231  228

 

到这里,一个单元的Y流终于处理完成了。天哪!是不是脑袋大得不得了?没办法,谁叫JPEG压缩这么优秀呢,吃点苦是必须的。接下来放松一下,我们来举个程序例子,进行图像的JPEG压缩:

 

 

编程举例:利用第三方控件进行JPEG压缩

在这个例子中,我们并不亲自去处理图像数据的压缩,而是交给一个第三方控件去处理,这样,我们利用别人写的这个控件,就能让我们的程序压缩图像了。当然,您如果是DIY的理想主义者,那么,你还得继续受点苦,继续往后面的部分看,最后,你也许不但实现了DIY,可能还能写出一个更优化的第三方控件,能够让更多的人来使用你的控件。

我们使用的控件是 IJL15.dll ,它是Intel公司提供的,如果您没有,就到网上下载一个。由于这个控件无法导入到VB6的工程中,因此必须把它存放在工程的同目录下,这一点千万要记住(如果生成EXE文件,也要把这个控件和EXE文件放在同一个目录下)。

VB6 建一个工程,窗体名为“BnpToJpeg”, ScaleMode =3-Pixel;一个文本框用来输入要打开的文件名或要保存的文件名;两个按钮;三个标签;图片框Picture1 用来显示打开的位图,Visible=false AutoRedraw=true AutoSize=true ScaleMode =3-Pixel BorderStyle=0-None

Save”按钮初始时Enabled=false ,待按“Open”打开Text1 输入的位图文件完成后,才变为可用状态,单击可以把原位图转换成JPEG文件(自动更换后缀名或另存为Text1中重新输入的文件名)

Option Explicit

 

Private Type RGBQUAD

    rgbBlue As Byte

    rgbGreen As Byte

    rgbRed As Byte

    rgbReserved As Byte

End Type

Private Type BITMAPINFOHEADER '40 bytes

    biSize As Long

    biWidth As Long

    biHeight As Long

    biPlanes As Integer

    biBitCount As Integer

    biCompression As Long

    biSizeImage As Long

    biXPelsPerMeter As Long

    biYPelsPerMeter As Long

    biClrUsed As Long

    biClrImportant As Long

End Type

Private Type BITMAPINFO    '24位位图信息头

    bmiHeader As BITMAPINFOHEADER

    bmiColors As RGBQUAD

End Type

Private Type JPEG_CORE_PROPERTIES_VB '导入控件的参数集

    UseJPEGPROPERTIES As Long

    DIBBytes As Long

    DIBWidth As Long

    DIBHeight As Long

    DIBPadBytes As Long

    DIBChannels As Long

    DIBColor As Long

    DIBSubsampling As Long

    JPGFile As Long

    JPGBytes As Long

    JPGSizeBytes As Long

    JPGWidth As Long

    JPGHeight As Long

    JPGChannels As Long

    JPGColor As Long

    JPGSubsampling As Long

    JPGThumbWidth As Long

    JPGThumbHeight As Long

    cconversion_reqd As Long

    upsampling_reqd As Long

    jquality As Long

    jprops(0 To 19999) As Byte

End Type

 

'控件操作函数(操作命令)

Private Declare Function ijlInit Lib "ijl15.dll" (jcprops As Any) As Long

Private Declare Function ijlFree Lib "ijl15.dll" (jcprops As Any) As Long

Private Declare Function ijlWrite Lib "ijl15.dll" (jcprops As Any, ByVal ioType As Long) As Long

 

'需要的API函数

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)

Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hDC As Long) As Long

Private Declare Function CreateDIBSection Lib "gdi32" (ByVal hDC As Long, pBitmapInfo As BITMAPINFO, ByVal un As Long, lplpVoid As Long, ByVal handle As Long, ByVal dw As Long) As Long

Private Declare Function SelectObject Lib "gdi32" (ByVal hDC As Long, ByVal hObject As Long) As Long

Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long

Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long

Dim fname As String '打开或保存的文件名

 

 

Private Sub Command1_Click()

Dim m_hDIb As Long

Dim m_hBmpOld As Long

Dim hDC As Long

Dim Ptr As Long

Dim BI As BITMAPINFO

Dim ww, hh As Integer '图像的宽和高

 

ww = Picture1.Width

hh = Picture1.Height

 

  Dim lhDC As Long

  Dim lhDCDesktop As Long

  Dim lhBmpOld As Long

 

    With BI.bmiHeader

        .biSize = Len(BI.bmiHeader)

        .biWidth = ww

        .biHeight = hh

        .biPlanes = 1

        .biBitCount = 24

        .biCompression = 0&

        .biSizeImage = ww * hh * 3

    End With

    hDC = CreateCompatibleDC(0)

    m_hDIb = CreateDIBSection(hDC, BI, 0, Ptr, 0, 0) '创建一幅内存图像并返回图像数据的内存地址

    m_hBmpOld = SelectObject(hDC, m_hDIb)

    BitBlt hDC, 0, 0, ww, hh, Picture1.hDC, 0, 0, vbSrcCopy

    SelectObject lhDC, lhBmpOld

    DeleteObject lhDC

   

   

  Dim tJ As JPEG_CORE_PROPERTIES_VB

  Dim bFile() As Byte

  Dim lptr As Long

  Dim lR As Long

  Dim sFile As String

 

  '检测保存文件名的正确性

fname = Text1.Text

Dim i, j As Integer

i = InStr(1, fname, "/")

Do While i > 0: j = i: i = InStr(1, fname, "/"): Loop

If Dir(Mid(fname, 1, j), vbDirectory) = "" Then Label1.Caption = "文件路径不存在": Exit Sub

 

If Right(fname, 4) <> ".jpg" Then fname = Mid(fname, 1, Len(fname) - 4) & ".jpg"

    lR = ijlInit(tJ)   '初始化控件

    If lR = 0 Then

        tJ.DIBWidth = ww

        tJ.DIBHeight = -hh

        tJ.DIBBytes = Ptr     '内存中图像数据的内存地址

        tJ.DIBPadBytes = 0

      

        bFile = StrConv(fname, vbFromUnicode)

        ReDim Preserve bFile(0 To UBound(bFile) + 1) As Byte

        bFile(UBound(bFile)) = 0

        lptr = VarPtr(bFile(0))

        CopyMemory tJ.JPGFile, lptr, 4  '把保存文件名导入参数集

        tJ.JPGWidth = ww

        tJ.JPGHeight = hh

        tJ.jquality = 50    '图像品质

 

        lR = ijlWrite(tJ, 8&)   '压缩JPEG文件

       

        ijlFree tJ  '释放控件内部参数

       

        Label1.Caption = "成功保存" & fname

    Else

        Label1.Caption = "没有找到 IJL15.dll 文件"

    End If

End Sub

 

Private Sub Command2_Click()

fname = Text1.Text

If Dir(fname) <> "" And Right(fname, 3) = "bmp" Then

Command1.Enabled = True

Picture1.Picture = LoadPicture(fname)

Else

Label1.Caption = "图片文件不存在或不是位图"

End If

End Sub

 

Private Sub Form_Load()

Text1.Text = App.Path & "/15.bmp"

End Sub

 

 

 

我们为了亲自动手写自己的JPEG压缩程序,还得继续了解JPEG文件的组成。

JPEG文件的组成包含以下几个部分:

 

组成:开头标志  APPO  DQT0  DQT1  SOFO  DC_Y_NV  AC_Y_NV  DC_UV_NV  AC_UV_NV 

大小: 2byte   18byte  69byte  69byte  19byte   33byte      183byte    33byte      183byte

标志:255 216  255 224  255 219        255 192   255 196

 

 

 

组成:   SOS          图像数据             结束标志

大小:   14byte                               2byte

标志:   255 224                              255 217

注:也有把后面这三块当成一块来理解,即SOF

    每个JPEG文件在图像数据的前面就要写上623个字节的各类必须的信息

 

 

 

 

 

 

 

 

 

 

 

 

 

 


JPEG文件中的一些标志是固定的,在解码时就是根据这些标志进行解读,在SOFO中的

HVY的值决定JPEG的压缩格式,在编程举例中会细说,这也是解码时的关键。

我们来看一个最简单的JPEG文件(一个16*16大小的红色方形图案,采用411的压缩格式,品质为50

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

255 216 255 224 0 16 74 70 73 70 0 1 1 1 0 72 0 72 0 0 255 219 0 67 0 16 11 12 14 12 10 16

14 13 14 18 17 16 19 24 40 26 24 22 22 24 49 35 37 29 40 58 51 61 60 57 51 56 55 64 72 92 78 64

68 87 69 55 56 80 109 81 87 95 98 103 104 103 62 77 113 121 112 100 120 92 101 103 99 255 219 0 67 1 17 18

18 24 21 24 47 26 26 47 99 66 56 66 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99

99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 255 192

0 17 8 0 16 0 16 3 1 34 0 2 17 1 3 17 1 255 196 0 31 0 0 1 5 1 1 1 1 1 1 0

0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 10 11 255 196 0 181 16 0 2 1 3 3 2 4 3 5

5 4 4 0 0 1 125 1 2 3 0 4 17 5 18 33 49 65 6 19 81 97 7 34 113 20 50 129 145 161 8 35

66 177 193 21 82 209 240 36 51 98 114 130 9 10 22 23 24 25 26 37 38 39 40 41 42 52 53 54 55 56 57 58

67 68 69 70 71 72 73 74 83 84 85 86 87 88 89 90 99 100 101 102 103 104 105 106 115 116 117 118 119 120 121 122

131 132 133 134 135 136 137 138 146 147 148 149 150 151 152 153 154 162 163 164 165 166 167 168 169 170 178 179 180 181 182 183

184 185 186 194 195 196 197 198 199 200 201 202 210 211 212 213 214 215 216 217 218 225 226 227 228 229 230 231 232 233 234 241

242 243 244 245 246 247 248 249 250 255 196 0 31 1 0 3 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1

2 3 4 5 6 7 8 9 10 11 255 196 0 181 17 0 2 1 2 4 4 3 4 7 5 4 4 0 1 2 119 0

1 2 3 17 4 5 33 49 6 18 65 81 7 97 113 19 34 50 129 8 20 66 145 161 177 193 9 35 51 82 240 21

98 114 209 10 22 36 52 225 37 241 23 24 25 26 38 39 40 41 42 53 54 55 56 57 58 67 68 69 70 71 72 73

74 83 84 85 86 87 88 89 90 99 100 101 102 103 104 105 106 115 116 117 118 119 120 121 122 130 131 132 133 134 135 136

137 138 146 147 148 149 150 151 152 153 154 162 163 164 165 166 167 168 169 170 178 179 180 181 182 183 184 185 186 194 195 196

197 198 199 200 201 202 210 211 212 213 214 215 216 217 218 226 227 228 229 230 231 232 233 234 242 243 244 245 246 247 248 249

250 255 218 0 12 3 1 0 2 17 3 17 0 63 0 197 162 138 43 202 62 240 255 0 255 217

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


大家看到上面文件中的标志了吗,其实真正图像数据只有(197 162 138 43 202 62 240 255 0 255后面的0 没有意义,有用的只有8Byte,转化为二进制为:

11000101  10100010  10001010  00101011  11001010  00111110  11110000  11111111

分解成:(查哈夫曼表,得到数值的实际长度)

110  00101  1010 00 1010 00 1010  00 1010   11110 01010 00  111110  111100 00

5  -26   E   0   E  0  E    0  E      5  -21 E   6   60   E

           4YU                      1UU        1VU  

E代表EBO,即一组64个数(一个U单位(Unit)))以0结束)由于图像的纯红色的块,所以在U中只有DC,所有的AC都为0

    411压缩格式为例,JPEG是这样来组织图像数据的:一块16*16大小的块中,4Y流的U(依次是上左,上右,下左,下右)和1U流的U1V流的U,依次处理并写入(Y流的UU流的UV流的U组成了一个MCU),再进入第二个16*16大小的块(MCU)中,依次类推。。。。。。

 

    编程举例:(编写JPEG压缩程序)

和上个程序中使用的差不多。用VB6 建一个工程,窗体名为“BnpToJpeg”, ScaleMode =3-Pixel;一个文本框用来输入要打开的文件名或要保存的文件名;两个按钮;三个标签;图片框Picture1 用来显示打开的位图,Visible=false AutoRedraw=true AutoSize=true ScaleMode =3-Pixel BorderStyle=0-None

Save”按钮初始时Enabled=false ,待按“Open”打开Text1 输入的位图文件完成后,才变为可用状态,单击可以把原位图转换成JPEG文件(自动更换后缀名或另存为Text1中重新输入的文件名)

 

'****本例程序采用411的格式进行JPEG压缩****

Option Explicit

 

Private Type tagHUFFCODE

 code As Long

 length As Byte

 val As Integer

End Type

 

Private Type tagACSYM

 zeroLen As Byte

 codeLen As Byte

 amplitude As Integer

End Type

 

Private Type tagSYM2

 amplitude As Integer

 codeLen As Byte

End Type

 

Private mask(15) As Long

Const maskstr As String = "1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768"

 

Private aanScaleFactor(7) As Single

Const aanScaleFactorstr As String = "1.0, 1.387039845, 1.306562965, 1.175875602,1.0, 0.785694958, 0.541196100, 0.275899379"

 

Private std_Y_QT(63) As Byte

Const std_Y_QTstr = " 16, 11, 10, 16, 24, 40, 51, 61,  12, 12, 14, 19, 26, 58, 60, 55,  14, 13, 16, 24, 40, 57, 69,56,  14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109,103,77,  24, 35, 55, 64, 81, 104,113,92,  49, 64, 78, 87, 103,121,120,101, 72, 92, 95, 98, 112,100,103,99"

 

Private std_UV_QT(63) As Byte

Const std_UV_QTstr = " 17, 18,  24, 47, 99, 99, 99, 99,  18, 21, 26, 66, 99, 99, 99, 99,  24, 26, 56, 99, 99, 99, 99, 99,  47, 66, 99 ,99, 99, 99, 99, 99,  99, 99, 99, 99, 99, 99, 99, 99,  99, 99, 99, 99, 99, 99, 99, 99,  99, 99, 99, 99, 99, 99, 99, 99,  99, 99, 99, 99, 99, 99, 99, 99"

 

Private FZBT(64) As Byte

Const FZBTstr = " 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"

 

Private COLORSPACECOEF(3, 2) As Single

Const COLORSPACECOEFstr As String = "1,0.25,0.25,1,1,1,1,0.5,0.5,1,0.5,0.5"

 

Private MCUIndex(3, 2) As Byte

Const MCUIndexstr As String = "4,1,1,1,1,1,2,1,1,2,1,1"

 

Private STD_DC_Y_NRCODES(15) As Byte

Const STD_DC_Y_NRCODESstr As String = "0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0"

Private STD_DC_Y_VALUES(11) As Byte

Const STD_DC_Y_VALUESstr As String = "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11"

Private STD_DC_UV_NRCODES(15) As Byte

Const STD_DC_UV_NRCODESstr As String = "0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0"

Private STD_DC_UV_VALUES(11) As Byte

Const STD_DC_UV_VALUESstr As String = "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11"

 

Private STD_AC_Y_NRCODES(15) As Byte

Const STD_AC_Y_NRCODESstr As String = "0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,125"

Private STD_AC_Y_VALUES(161) As Byte

Const STD_AC_Y_VALUESstr As String = "1,2,3,0,4,17,5,18,33,49,65,6,19,81,97,7,34,113,20,50,129,145,161,8,35,66,177,193,21,82,209,240,36,51,98,114,130,9,10,22,23,24,25,26,37,38,39,40,41,42,52,53,54,55,56,57,58,67,68,69,70,71,72,73,74,83,84,85,86,87,88,89,90,99,100,101,102,103,104,105,106,115,116,117,118,119,120,121,122,131,132,133,134,135,136,137,138,146,147,148,149,150,151,152,153,154,162,163,164,165,166,167,168,169,170,178,179,180,181,182,183,184,185,186,194,195,196,197,198,199,200,201,202,210,211,212,213,214,215,216,217,218,225,226,227,228,229,230,231,232,233,234,241,242,243,244,245,246,247,248,249,250"

Private STD_AC_UV_NRCODES(15) As Byte

Const STD_AC_UV_NRCODESstr As String = "0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,119"

Private STD_AC_UV_VALUES(161) As Byte

Const STD_AC_UV_VALUESstr As String = "0,1,2,3,17,4,5,33,49,6,18,65,81,7,97,113,19,34,50,129,8,20,66,145,161,177,193,9,35,51,82,240,21,98,114,209,10,22,36,52,225,37,241,23,24,25,26,38,39,40,41,42,53,54,55,56,57,58,67,68,69,70,71,72,73,74,83,84,85,86,87,88,89,90,99,100,101,102,103,104,105,106,115,116,117,118,119,120,121,122,130,131,132,133,134,135,136,137,138,146,147,148,149,150,151,152,153,154,162,163,164,165,166,167,168,169,170,178,179,180,181,182,183,184,185,186,194,195,196,197,198,199,200,201,202,210,211,212,213,214,215,216,217,218,226,227,228,229,230,231,232,233,234,242,243,244,245,246,247,248,249,250"

 

'***由于JPEG块中Integer值在保存时,高低位互换,为转化方便,这里用两个Byte表示,并注明(是高低位互换的Integer)

Private Type tagJPEGAPP0 '18 bytes

 segmentTag(1) As Byte   'APP0段标记,必须为 255 224(是高低位互换的Integer)

 length(1) As Byte     '段长度,一般为16,如果没有缩略图(是高低位互换的Integer)

 id(4) As Byte      '文件标记 "JFIF" + "/0",值为 74 70 73 70 0

 ver(1) As Byte        '文件版本,一般为01, 0101, 02(是高低位互换的Integer)

 densityUnit As Byte  '密度单位,0=无单位 1=点数/英寸 2=点数/厘米

 densityX(1) As Byte    'X轴方向密度,通常写0,72(是高低位互换的Integer)

 densityY(1) As Byte    'Y轴方向密度,通常写0,72(是高低位互换的Integer)

 thp As Byte      '缩略图水平像素数,0

 tvp As Byte      '缩略图垂直像素数,0

End Type

 

Private Type tagJPEGDQT_8BITS '69 bytes

 segmentTag(1) As Byte   'DQT段标记,必须为225,219(是高低位互换的Integer)

 length(1) As Byte     '段长度,这里是 67(是高低位互换的Integer)

 tableInfo As Byte   '量化表信息

 table(63) As Byte   '量化表(8 bits)

End Type

 

Private Type tagJPEGSOF0_24BITS '19 bytes

 segmentTag(1) As Byte    'SOF段标记,必须为255 192(是高低位互换的Integer)

 length(1) As Byte      '段长度,真彩图为17,灰度图为11(是高低位互换的Integer)

 precision As Byte   '精度,每个信号分量所用的位数,基本系统为0x08

 heights(1) As Byte      '图像高度(是高低位互换的Integer)

 widths(1) As Byte       '图像宽度(是高低位互换的Integer)

 sigNum As Byte    '信号数量,真彩JPEG应该为3,灰度为1

 YID As Byte      '信号编号,亮度Y

 HVY As Byte      '采样方式,0-3位是垂直采样,4-7位是水平采样

 QTY As Byte      '对应量化表号

 UID As Byte      '信号编号,色差U

 HVU As Byte      '采样方式,0-3位是垂直采样,4-7位是水平采样

 QTU As Byte      '对应量化表号

 VID As Byte     '信号编号,色差V

 HVV As Byte      '采样方式,0-3位是垂直采样,4-7位是水平采样

 QTV As Byte      '对应量化表号

End Type

 

Private Type tagJPEGDHT ' bytes

 segmentTag(1) As Byte    'DHT段标记,必须为255 196(是高低位互换的Integer)

 length(1) As Byte      '段长度(是高低位互换的Integer)

 tableInfo As Byte   '表信息,基本系统中 bit0-3 Huffman表的数量,bit4 0DCHuffman 1ACHuffman表,bit5-7保留,必须为0

  '1-16位的Huffman码字的数量,分别存放在数组[1-16],BYTE* huffVal依次存放各码字对应的值

End Type

 

Private Type tagJPEGSOS_24BITS '14 bytes

 segmentTag(1) As Byte    'SOS段标记,必须为255 218(是高低位互换的Integer)

 length(1) As Byte    '段长度,这里是12(是高低位互换的Integer)

 sigNum As Byte    '信号分量数,真彩图为0x03,灰度图为0x01

 YID As Byte      '亮度Y信号ID,这里是1

 HTY As Byte      'Huffman表号,bit0-3DC信号的表,bit4-7AC信号的表

 UID As Byte      '亮度Y信号ID,这里是2

 HTU As Byte

 VID As Byte      '亮度Y信号ID,这里是3

 HTV As Byte

 Ss As Byte      '基本系统中为0

 Se As Byte      '基本系统中为63

 Bf As Byte      '基本系统中为0

End Type

 

Private YQT(63) As Byte

Private UVQT(63) As Byte

 

Private YQT_DCT(63) As Single

Private UVQT_DCT(63) As Single

 

Private STD_DC_Y_HT(11) As tagHUFFCODE

Private STD_DC_UV_HT(11) As tagHUFFCODE

Private STD_AC_Y_HT(255) As tagHUFFCODE

Private STD_AC_UV_HT(255) As tagHUFFCODE

 

Dim fname As String

Dim ww, hh As Integer

Dim Ybuf(), Ubuf, Vbuf() As Integer

Dim Q As Integer     '图像品质 0-100

Dim bytepos, bytenew As Byte

 

 

Private Sub splitstring(by() As Byte, str As String) 'Byte数组赋值

Dim st() As String

st = Split(str, ",")

Dim i, l As Integer

l = UBound(st)

For i = 0 To l: by(i) = CByte(st(i)): Next

End Sub

 

Private Sub splitstringB(by() As Single, str As String) 'Single数组赋值

Dim st() As String

st = Split(str, ",")

Dim i, l As Integer

l = UBound(st)

For i = 0 To l: by(i) = CSng(st(i)): Next

End Sub

 

Private Sub splitstringC(by() As Long, str As String) 'Long数组赋值

Dim st() As String

st = Split(str, ",")

Dim i, l As Integer

l = UBound(st)

For i = 0 To l: by(i) = CSng(st(i)): Next

End Sub

 

Private Sub getY_QT_UV_QT()

Dim i, tempV1, tempV2 As Integer

splitstring FZBT, FZBTstr

splitstring std_Y_QT, std_Y_QTstr

splitstring std_UV_QT, std_UV_QTstr

If Q < 50 Then Q = 5000 / Q Else Q = 200 - Q * 2

For i = 0 To 63

tempV1 = std_Y_QT(i) * Q / 100

If tempV1 < 1 Then tempV1 = 1 Else If tempV1 > 255 Then tempV1 = 255

YQT(FZBT(i)) = tempV1

tempV2 = std_UV_QT(i) * Q / 100

If tempV2 < 1 Then tempV2 = 1 Else If tempV2 > 255 Then tempV2 = 255

UVQT(FZBT(i)) = tempV2

Next

End Sub

 

Private Sub InitQTForAANDCT()

splitstringB aanScaleFactor, aanScaleFactorstr

Dim i, j, k As Integer

For i = 0 To 7

For j = 0 To 7

YQT_DCT(k) = CSng(1 / (YQT(k) * aanScaleFactor(i) * aanScaleFactor(j) * 8))

UVQT_DCT(k) = CSng(1 / (UVQT(k) * aanScaleFactor(i) * aanScaleFactor(j) * 8))

k = k + 1

Next

Next

End Sub

 

Private Sub BuildSTDHuffTab(nrcodes() As Byte, stdTab() As Byte, huffCode() As tagHUFFCODE)

Dim i, j, k  As Integer

Dim code As Long

For i = 1 To 16

For j = 1 To nrcodes(i - 1)

    huffCode(stdTab(k)).code = code

    huffCode(stdTab(k)).length = i

    k = k + 1

    code = code + 1

Next

code = code * 2

Next

For i = 0 To k - 1: huffCode(i).val = stdTab(i): Next

 

End Sub

 

Private Sub Command1_Click()

fname = Text1.Text

If Dir(fname) <> "" And Right(fname, 3) = "bmp" Then

Command1.Enabled = True

Picture1.Picture = LoadPicture(fname)

 

ww = Picture1.Width

hh = Picture1.Height

If ww Mod 16 > 0 Then ww = ww + (16 - ww Mod 16) '补足16的倍数

If hh Mod 16 > 0 Then hh = hh + (16 - hh Mod 16)

Q = 50

Else

Label1.Caption = "图片文件不存在或不是位图"

End If

 

End Sub

 

Private Sub Command2_Click()

     '<<< 初始化所需量化表 >>>

getY_QT_UV_QT  'std_Y_QT >>Y_QT, std_UV_QT >>UV_QT

InitQTForAANDCT  'Y_QY >>YQT_DCT, UV_QT >>UVQT_DCT  (as single)

 

'STD_DC_Y_NRCODES,  STD_DC_Y_VALUES >>STD_DC_Y_HT (as tagHUFFCODE)

'STD_AC_Y_NRCODES,  STD_AC_Y_VALUES >>STD_AC_Y_HT

'STD_DC_UV_NRCODES,  STD_DC_UV_VALUES >>STD_DC_UV_HT

'STD_AC_UV_NRCODES,  STD_AC_UV_VALUES >>STD_AC_UV_HT

splitstring STD_DC_Y_NRCODES, STD_DC_Y_NRCODESstr

splitstring STD_DC_Y_VALUES, STD_DC_Y_VALUESstr

splitstring STD_AC_Y_NRCODES, STD_AC_Y_NRCODESstr

splitstring STD_AC_Y_VALUES, STD_AC_Y_VALUESstr

splitstring STD_DC_UV_NRCODES, STD_DC_UV_NRCODESstr

splitstring STD_DC_UV_VALUES, STD_DC_UV_VALUESstr

splitstring STD_AC_UV_NRCODES, STD_AC_UV_NRCODESstr

splitstring STD_AC_UV_VALUES, STD_AC_UV_VALUESstr

BuildSTDHuffTab STD_DC_Y_NRCODES, STD_DC_Y_VALUES, STD_DC_Y_HT

BuildSTDHuffTab STD_AC_Y_NRCODES, STD_AC_Y_VALUES, STD_AC_Y_HT

BuildSTDHuffTab STD_DC_UV_NRCODES, STD_DC_UV_VALUES, STD_DC_UV_HT

BuildSTDHuffTab STD_AC_UV_NRCODES, STD_AC_UV_VALUES, STD_AC_UV_HT

 

splitstringC mask, maskstr   '>>mask  (as long)

 

 

     '<<< JPG文件头 >>>

Dim SOIT(1) As Byte

Dim i, j, p, k As Integer

 

'检测保存文件名的正确性

fname = Text1.Text

Dim i, j As Integer

i = InStr(1, fname, "/")

Do While i > 0: j = i: i = InStr(1, fname, "/"): Loop

If Dir(Mid(fname, 1, j), vbDirectory) = "" Then Label1.Caption = "文件路径不存在": Exit Sub

 

Open fname For Binary As #1

'SOIT

splitstring SOIT, "255,216"  'JPEG文件开始标志 255 216

Put #1, , SOIT

 

'setJPEGAPPO

Dim jappo As tagJPEGAPP0

splitstring jappo.segmentTag, "255,224"

splitstring jappo.length, "0,16"

jappo.id(0) = 74               '"JFIF0"

jappo.id(1) = 70

jappo.id(2) = 73

jappo.id(3) = 70

jappo.id(4) = 0

splitstring jappo.ver, "1,1"            '版本号 1 1

jappo.densityUnit = 1

splitstring jappo.densityX, "0,72"

splitstring jappo.densityY, "0,72"

jappo.thp = 0

jappo.tvp = 0

Put #1, , jappo

 

'setJPEGQT

Dim dqt As tagJPEGDQT_8BITS

splitstring dqt.segmentTag, "255,219"

splitstring dqt.length, "0,67"

dqt.tableInfo = 0

For i = 0 To 63: dqt.table(i) = YQT(i): Next

Put #1, , dqt

splitstring dqt.segmentTag, "255,219"

splitstring dqt.length, "0,67"

dqt.tableInfo = 1

For i = 0 To 63: dqt.table(i) = UVQT(i): Next

Put #1, , dqt

 

'setJPEGSOF0_24BITS

Dim sof As tagJPEGSOF0_24BITS

splitstring sof.segmentTag, "255,192"

splitstring sof.length, "0,17"

sof.precision = 8

splitstring sof.heights, Int(Picture1.Height / 256) & "," & (Picture1.Height Mod 256)

splitstring sof.widths, Int(Picture1.Width / 256) & "," & (Picture1.Width Mod 256)

sof.sigNum = 3

sof.YID = 1

sof.QTY = 0

sof.UID = 2

sof.QTU = 1

sof.VID = 3

sof.QTV = 1

sof.HVU = 17

sof.HVV = 17

sof.HVY = 34

Put #1, , sof

 

'setJPEGDHT

Dim dht As tagJPEGDHT

splitstring dht.segmentTag, "255,196"

splitstring dht.length, "0," & 31

dht.tableInfo = 0

Put #1, , dht

Put #1, , STD_DC_Y_NRCODES

Put #1, , STD_DC_Y_VALUES

 

splitstring dht.length, "0," & 181

dht.tableInfo = 16

Put #1, , dht

Put #1, , STD_AC_Y_NRCODES

Put #1, , STD_AC_Y_VALUES

 

splitstring dht.length, "0," & 31

dht.tableInfo = 1

Put #1, , dht

Put #1, , STD_DC_UV_NRCODES

Put #1, , STD_DC_UV_VALUES

 

splitstring dht.length, "0," & 181

dht.tableInfo = 17

Put #1, , dht

Put #1, , STD_AC_UV_NRCODES

Put #1, , STD_AC_UV_VALUES

 

'setJPEGSOS_24BITS

Dim sos As tagJPEGSOS_24BITS

splitstring sos.segmentTag, "255,218"

splitstring sos.length, "0,12"

sos.sigNum = 3

sos.YID = 1

sos.HTY = 0

sos.UID = 2

sos.HTU = 17

sos.VID = 3

sos.HTV = 17

sos.Se = 63

sos.Ss = 0

sos.Bf = 0

Put #1, , sos

 

 

 

     '<<< 处理图像数据,写入文件 >>>

GetYUVbuf

Get8x8YUVbuf

bytepos = 7: bytenew = 0

 

Dim dctBuf(63) As Integer

Dim dctint(63) As Integer

Dim mcuNum As Integer

Dim ydc, udc, vdc As Integer

mcuNum = ww * hh / 256

ydc = 0

udc = 0

vdc = 0

 

For i = 0 To mcuNum - 1

  For k = 0 To 3

    p = (i * 4 + k) * 64

    For j = 0 To 63: dctBuf(j) = Ybuf(p + j): Next

    FDCT dctBuf, dctint, True            '离散余弦变换并量化

    ydc = ProcessDU(dctint, True, ydc) '压缩处理并写入文件

  Next

 

    p = i * 64

    For j = 0 To 63: dctBuf(j) = Ubuf(p + j): Next

    FDCT dctBuf, dctint, False

    udc = ProcessDU(dctint, False, udc)

 

 

    For j = 0 To 63: dctBuf(j) = Vbuf(p + j): Next

    FDCT dctBuf, dctint, False

    vdc = ProcessDU(dctint, False, vdc)

 

Next

 

WriteEndBits

 

     '<<< 写入文件结束标志 >>>

Dim wbyte As Byte

wbyte = 255

Put #1, , wbyte

wbyte = 217

Put #1, , wbyte '255 217

Close #1

 

Label1.Caption = "成功保存" & fname

End Sub

 

Private Sub GetYUVbuf() '获得图像的YUV

Dim i, j, poin As Integer

Dim psize, k, kk, col As Long

Dim tempr, tempg, tempb As Byte

Dim tempy, tempu, tempv As Integer

Dim wid0 As Boolean

 

psize = ww * hh

ReDim Ybuf(psize - 1)

ReDim Ubuf(psize / 4 - 1)

ReDim Vbuf(psize / 4 - 1)

 

For i = 0 To hh - 1 '正向读取

'判断是否0246...

If i Mod 2 = 0 Then wid0 = True Else wid0 = False

For j = 0 To ww - 1

col = Picture1.Point(j, i)

Select Case col     '为提高运算速度而写

Case -1, 0

tempr = 0: tempg = 0: tempb = 0

Case 16777215

tempr = 255: tempg = 255: tempb = 255

Case Else

tempb = Int(col / 65536)

col = col Mod 65536

tempg = Int(col / 256)

tempr = col Mod 256

End Select

 

tempy = Int(0.299 * tempr + 0.587 * tempg + 0.114 * tempb) - 128

If tempy < -128 Then tempy = -128 Else If tempy > 127 Then tempy = 127

Ybuf(k) = tempy

k = k + 1

 

If wid0 = True And j Mod 2 = 0 Then

tempu = Int(-0.1687 * tempr - 0.3313 * tempg + 0.5 * tempb)

tempv = Int(0.5 * tempr - 0.4187 * tempg - 0.0813 * tempb)

If tempu < -128 Then tempu = -128 Else If tempu > 127 Then tempu = 127

If tempv < -128 Then tempv = -128 Else If tempv > 127 Then tempv = 127

Ubuf(kk) = tempu

Vbuf(kk) = tempv

kk = kk + 1

End If

Next

Next

 

End Sub

 

Private Sub Get8x8YUVbuf() 'YUV流分割成 8 x 8 的块

Dim i, j, k, x, y As Integer

Dim kid, ws, hs As Integer

Dim poin, p, po, poi, lsize As Integer

Dim tempy(), tempu(), tempv() As Integer

 

'****处理Ybuf****

ws = ww / 16    '图像宽有几个16*16的块数

hs = hh / 16    '图像高有几个16*16的块数

lsize = ww * 16   '16行像素总数

ReDim tempy(lsize - 1)

For i = 0 To hs - 1

For j = 0 To ws - 1

'设置16*16块中第1 像素个在整个流中的位置

poin = i * lsize + j * 16

For k = 0 To 3  '分别处理16*16块中的48*8的块

'设置块8*8中第1 像素个在整个流中的位置

Select Case k

Case 0

poi = poin

Case 1

poi = poin + 8

Case 2

poi = poin + ww * 8

Case 3

poi = poin + ww * 8 + 8

End Select

 

For y = 0 To 7

po = poi + y * ww

For x = 0 To 7: p = (j * 4 + k) * 64 + y * 8 + x: tempy(p) = Ybuf(po + x): Next

Next y

 

Next k

Next j

poin = i * lsize

For x = 0 To lsize - 1:  Ybuf(poin + x) = tempy(x):   Next

Next i

 

 

'****处理Ubuff  Vbuff****

lsize = ww / 2 * 8 'UV流的长度是Y流的四分之一

ReDim tempu(lsize - 1)

ReDim tempv(lsize - 1)

For i = 0 To hs - 1

poin = lsize * i

For j = 0 To ws - 1

poi = poin + j * 8

 

For y = 0 To 7

po = poi + y * (ww / 2)

For x = 0 To 7

p = j * 64 + y * 8 + x

tempu(p) = Ubuf(po + x)

tempv(p) = Vbuf(po + x)

Next x

Next y

 

Next j

For x = 0 To lsize - 1: Ubuf(poin + x) = tempu(x): Vbuf(poin + x) = tempv(x): Next

Next i

End Sub

 

'图像数据压缩处理并写入文件

Private Function ProcessDU(sigbuf() As Integer, ByVal YorUV As Boolean, ByVal dc As Integer) As Integer

Dim i, j As Integer

Dim diffVal As Integer

Dim acLen As Byte

Dim acSym(63) As tagACSYM

Dim Symbol As tagSYM2

Dim temphuf As tagHUFFCODE

 

 

 

 

  'DC信号编码,写入文件

  '-------------------------------------------------

  diffVal = sigbuf(0) - dc

  ProcessDU = sigbuf(0)

 

 

  If diffVal = 0 Then

  '选择用STD_DC_Y_HTSTD_DC_UV_HT表进行搜索

  If YorUV = True Then temphuf = STD_DC_Y_HT(0) Else temphuf = STD_DC_UV_HT(0)

   '写入0到位流(写入(01),由于是位操作,写入长度1位(Bite),值为0,结果就是写入0到位流)

  WriteBits temphuf.code, temphuf.length

  Else

  'STD_DC_Y_HT(ComputeVLI(diffVal)) DC差异值>> 转化为二进制长度值,作为索引>> 哈夫曼表的值

  If YorUV = True Then temphuf = STD_DC_Y_HT(ComputeVLI(diffVal)) Else temphuf = STD_DC_UV_HT(ComputeVLI(diffVal))

  '写入长度为表中 .length位,值为 .code   到位流

  WriteBits temphuf.code, temphuf.length

 

  Symbol.codeLen = ComputeVLI(diffVal)               ' 获取差异值二进制编码长度

  If diffVal >= 0 Then Symbol.amplitude = diffVal Else Symbol.amplitude = pow(diffVal) '计算反码

  WriteBits Symbol.amplitude, Symbol.codeLen   '写入DC差异值或差异值反码到位流

  End If

 

 

  'AC信号编码并写入文件

  '------------------------------------------------

  For i = 63 To 1 Step -1

      If sigbuf(i) <> 0 Then Exit For

  Next   '判断ac信号是否全为0

  If i > 0 Then

      acLen = RLEComp(sigbuf, acSym)       'AC进行熵编码

      For j = 0 To acLen - 1         '依次对AC中间符号Huffman编码

          If acSym(j).codeLen = 0 Then          '是否有连续160

               If YorUV = True Then temphuf = STD_AC_Y_HT(240) Else temphuf = STD_AC_UV_HT(240)

               WriteBits temphuf.code, temphuf.length       '写入(15,0,0)

          Else

              '0个数和非0数二进制长度组合起来的值为索引,写入哈夫曼表的值

               If YorUV = True Then temphuf = STD_AC_Y_HT(acSym(j).zeroLen * 16 + acSym(j).codeLen) Else temphuf = STD_AC_UV_HT(acSym(j).zeroLen * 16 + acSym(j).codeLen)

               WriteBits temphuf.code, temphuf.length

    

               '写入非0的值或反码

               Symbol.codeLen = ComputeVLI(acSym(j).amplitude)               '获取编码长度

               If acSym(j).amplitude >= 0 Then Symbol.amplitude = acSym(j).amplitude Else Symbol.amplitude = pow(acSym(j).amplitude) '计算反码

               WriteBits Symbol.amplitude, Symbol.codeLen

          End If

      Next

   End If

     

    '写入块结束标记

    '------------------------------------------------

   If i < 63 Then

   If YorUV = True Then temphuf = STD_AC_Y_HT(0) Else temphuf = STD_AC_UV_HT(0)

   WriteBits temphuf.code, temphuf.length  '(0,0)

   End If

End Function

 

 ' 8x8的浮点离散余弦变换,并且量化

Private Sub FDCT(lpBuff() As Integer, lpint() As Integer, ByVal YorUV As Boolean)

Dim tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 As Integer

Dim tmp10, tmp11, tmp12, tmp13 As Integer

Dim z1, z2, z3, z4, z5, z11, z13 As Integer

Dim dataptr(7) As Integer

Dim ctr, i, p As Integer

 

'对行进行计算

  For ctr = 7 To 0 Step -1

  p = ctr * 8

  For i = 0 To 7: dataptr(i) = lpBuff(p + i): Next

   tmp0 = dataptr(0) + dataptr(7)

   tmp7 = dataptr(0) - dataptr(7)

   tmp1 = dataptr(1) + dataptr(6)

   tmp6 = dataptr(1) - dataptr(6)

   tmp2 = dataptr(2) + dataptr(5)

   tmp5 = dataptr(2) - dataptr(5)

   tmp3 = dataptr(3) + dataptr(4)

   tmp4 = dataptr(3) - dataptr(4)

   ' 对偶数项进行运算

   tmp10 = tmp0 + tmp3

   tmp13 = tmp0 - tmp3

   tmp11 = tmp1 + tmp2

   tmp12 = tmp1 - tmp2

   dataptr(0) = tmp10 + tmp11

   dataptr(4) = tmp10 - tmp11

   z1 = (tmp12 + tmp13) * 0.707106781

   dataptr(2) = tmp13 + z1

   dataptr(6) = tmp13 - z1

   '对奇数项进行计算

   tmp10 = tmp4 + tmp5

   tmp11 = tmp5 + tmp6

   tmp12 = tmp6 + tmp7

   z5 = (tmp10 - tmp12) * 0.382683433

   z2 = 0.5411961 * tmp10 + z5

   z4 = 1.306562965 * tmp12 + z5

   z3 = tmp11 * 0.707106781

   z11 = tmp7 + z3

   z13 = tmp7 - z3

   dataptr(5) = z13 + z2

   dataptr(3) = z13 - z2

   dataptr(1) = z11 + z4

   dataptr(7) = z11 - z4

   For i = 0 To 7:  lpint(p + i) = dataptr(i): Next

  Next

 

 

'对列进行计算

  For ctr = 7 To 0 Step -1

 

  For i = 0 To 7: dataptr(i) = lpint(i * 8 + ctr): Next

   tmp0 = dataptr(0) + dataptr(7)

   tmp7 = dataptr(0) - dataptr(7)

   tmp1 = dataptr(1) + dataptr(6)

   tmp6 = dataptr(1) - dataptr(6)

   tmp2 = dataptr(2) + dataptr(5)

   tmp5 = dataptr(2) - dataptr(5)

   tmp3 = dataptr(3) + dataptr(4)

   tmp4 = dataptr(3) - dataptr(4)

   ' 对偶数项进行运算

   tmp10 = tmp0 + tmp3

   tmp13 = tmp0 - tmp3

   tmp11 = tmp1 + tmp2

   tmp12 = tmp1 - tmp2

   dataptr(0) = tmp10 + tmp11

   dataptr(4) = tmp10 - tmp11

   z1 = (tmp12 + tmp13) * 0.707106781

   dataptr(2) = tmp13 + z1

   dataptr(6) = tmp13 - z1

   '对奇数项进行计算

   tmp10 = tmp4 + tmp5

   tmp11 = tmp5 + tmp6

   tmp12 = tmp6 + tmp7

   z5 = (tmp10 - tmp12) * 0.382683433

   z2 = 0.5411961 * tmp10 + z5

   z4 = 1.306562965 * tmp12 + z5

   z3 = tmp11 * 0.707106781

   z11 = tmp7 + z3

   z13 = tmp7 - z3

   dataptr(5) = z13 + z2

   dataptr(3) = z13 - z2

   dataptr(1) = z11 + z4

   dataptr(7) = z11 - z4

   For i = 0 To 7:  lpint(i * 8 + ctr) = dataptr(i): Next

  Next

 

 

 

 

  '量化

  '--------------------------------------------------------

  For i = 0 To 63

   '选择用YQT_DCTUVQT_DCT表进行量化

  If YorUV = True Then

  lpint(FZBT(i)) = CInt(lpint(i) * YQT_DCT(i))

  Else

  lpint(FZBT(i)) = CInt(lpint(i) * UVQT_DCT(i))

  End If

  Next

 

End Sub

 

'使用RLE算法(熵编码)对AC压缩 , 假设输入数据1, 0, 0, 0, 3, 0, 5

'输出为(0,1,1)(3,2,3)(1,1,5)

'左位表示非0的数前面0的个数,中位表示左位值的二进制长度,右位表示非0的值

'0的个数超过表示范围则输出为(15,0) ,其余的0数据在下一个符号中表示.

Private Function RLEComp(lpbuf() As Integer, lpOutBuf() As tagACSYM) As Byte

Dim zeroNum As Byte

Dim EOBPos As Byte

Dim i, j As Byte

EOBPos = 63

For i = 63 To 0 Step -1  '从最后的AC信号数0的个数

If lpbuf(i) = 0 Then EOBPos = EOBPos - 1 Else Exit For

Next

For i = 1 To EOBPos  '从第二个信号,即AC信号开始编码

If lpbuf(i) = 0 And zeroNum < 15 Then

zeroNum = zeroNum + 1

Else

    lpOutBuf(j).zeroLen = zeroNum        '0行程(连续长度)

    lpOutBuf(j).codeLen = ComputeVLI(lpbuf(i))    '振幅编码长度

    lpOutBuf(j).amplitude = lpbuf(i)      '振幅

    zeroNum = 0            '0计数器复位

    j = j + 1         '符号计数

End If

Next

RLEComp = j

End Function

 

'获得数值的二进制长度

Private Function ComputeVLI(ByVal val As Integer) As Byte

val = Abs(val)

Select Case val

Case 0

  ComputeVLI = 0

Case 1

  ComputeVLI = 1

Case Is < 4

  ComputeVLI = 2

Case Is < 8

  ComputeVLI = 3

Case Is < 16

  ComputeVLI = 4

Case Is < 32

  ComputeVLI = 5

Case Is < 64

  ComputeVLI = 6

Case Is < 128

  ComputeVLI = 7

Case Is < 256

  ComputeVLI = 8

Case Is < 512

  ComputeVLI = 9

Case Is < 1024

  ComputeVLI = 10

Case Else

  ComputeVLI = 11

End Select

End Function

 

'获得反码,如:数值为 -5(绝对值的二进制为 101),那么反码为 2(二进制为 010,二进制上同位上取反值)

Private Function pow(ByVal val As Integer) As Integer

Dim i, l As Byte

Dim sz As Integer

val = Abs(val)

l = ComputeVLI(val)

 

For i = l - 1 To 0 Step -1

If val >= mask(i) Then

val = val - mask(i)

Else

sz = sz + mask(i)

End If

Next

pow = sz

End Function

 

'把要写入的值组成 Bit 流,并(每8个)转化成Byte写入文件

Private Sub WriteBits(ByVal value As Long, ByVal codeLen As Byte)

Dim posval As Integer

Dim wbyte As Byte

posval = codeLen - 1

Do While posval >= 0

If (value And mask(posval)) Then bytenew = bytenew + mask(bytepos)

posval = posval - 1

bytepos = bytepos - 1

If bytepos < 0 Then

If bytenew = 255 Then

wbyte = 255

Put #1, , wbyte

wbyte = 0

Put #1, , wbyte

Else

wbyte = bytenew

Put #1, , wbyte

End If

bytepos = 7

bytenew = 0

End If

Loop

End Sub

 

'图像结束时,写入Bit 流中余下的Bit

Private Sub WriteEndBits()

Dim wbyte As Byte

If bytepos > 0 Then

Do While bytepos >= 0

bytenew = bytenew + mask(bytepos)

bytepos = bytepos - 1

If bytepos < 0 Then

If bytenew = 255 Then

wbyte = 255

Put #1, , wbyte

wbyte = 0

Put #1, , wbyte

Else

wbyte = bytenew

Put #1, , wbyte

End If

End If

Loop

End If

End Sub

 

补充说明:灰度JPEG压缩(类似于黑白照片)

灰度JPEG压缩有别于黑和白,它有256个不同程度的灰度色彩。它其实就是在JPEG压缩过程中,只保留Y流的压缩,忽略了U流和V流的压缩。

灰度JPEG文件与彩色JPEG文件的区别:

1.              灰度JPEG文件中也只有Y流的信息,它只支持111的压缩格式,不支持其他的格式。

2.              在文件头中,SOFO只有 13个字节,仅保留Y的信息,去掉UV的信息(sigNum=1),SOS只有10个字节(sigNum=1)。

3.              量化表可以去掉一个,哈夫曼表可以去掉两个,不去掉也加以。

4.              在图像数据中每个MCU中只有Y信息。

限于篇幅关系,我就不写源码,有兴趣的朋友可以自己去写。

 

                            

童跃   福建省华安县际头小学

tongyue2007@126.com

2007 5 1

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tongyue

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值