[轉]解讀GIF檔案

來源:互聯網
上載者:User

轉http://blog.csdn.net/tongyue/archive/2007/07/30/1716572.aspx

 

告訴您:GIF這種壓縮標準是如何進行計算的,以及動手壓縮圖片

 

我們知道,位元影像檔案和表徵圖檔案都是以像素為單位進行映像資訊記錄的,這樣的記錄方式產生的檔案十分龐大,稱為未壓縮檔,在單機上使用是可以的,如果要在網上傳播是不受歡迎的。如何對映像進一步壓縮呢?GIF就是常用的壓縮技術,也是網上流行的影像檔格式。

 

GIF壓縮標準:

GIF作為一種壓縮標準,受到了很多軟體公司的普遍支援,當然也包含微軟公司。GIF有GIF87a 和GIF89a兩種版本,而且是早期動畫使用的格式。

GIF是如何進行壓縮的?它首先把映像轉化成8位的256色映像(或128色、64色、32色、16色、8 色、4色、2色都可以),以256色為例,表示一個像素就需要8位的二進位資訊,即1個位元組Byte。接著GIF根據映像中像素的排列規律,建立一個類似於顏色排列表,用一個個9位的整數值(Integer)來表示像素的排列規律,達到壓縮的目的(它的名字叫LZW 壓縮演算法)。聽起來是不是不好理解,後面我會舉例來說明的。最後在儲存到檔案時,再把9位的整數(表示範圍為0~511)轉換成8位的Byte類型的值,儲存到GIF檔案中。

 

顏色排列表:

假設有一串映像的像素流(儲存在byte數實值型別的buf () 數組中)進行GIF壓縮,這串資料流的前20個,即buf (0) ~ buf (19) 的值分別為:

8  8  8  8  99  99  8   8  8  8   

8  8  8  99  99  8  99  8  99  8

資料流的第一個數值被記錄在檔案的資訊部分,真正的壓縮計算都是從第二個開始的:

運算的程式碼:

bint(0) = 256

    sP = Right$("00" & buf(0), 3)

    iCode = buf(0)

For i = 1 To 19

       …

       …

            sB = Right$("00" & buf(i), 3)

            sP = sP & sB

            On Error Resume Next

            iCode = colTable(sP)  ’ 集合中是否已經儲存了需要的成員

            If Err <> 0 Then  ’沒有找到時

                n = colTable.Count

                colTable.Add n + 258, sP  ’ 新增成員及索引

                j = j + 1

                bint(j) = iCode  ’儲存轉化後的值到要儲存的整數集合中

                sP = sB

                iCode = buf(i) 

                Err.Clear

            End If

           

    Next

運算過程中各變數的變化為:

buf()序號     colTable               bint()值

           成員   索引

                                     256

1          258   008008              8

3          259   008008008           258

4          260   008099              8

5          261   099099              99

6          262   099008              99

9          263   008008008008        259

13         264   008008008008099     263

15         265   099099008           261

17         266   008099008           260

                                     260

由上面計算的結果看,可以說明幾個問題:

1.  檔案被壓縮了,20個byte 被壓縮成10個9位的Integer ,這是因為顏色的規律被儲存在顏色排列表的集合中,用一個數值就能同時表示幾個像素的資訊。

2.  壓縮後的檔案大小除了和映像的尺寸(像素流的長短)有關係以外,映像本身的複雜程度也影響著壓縮檔的大小,映像越簡單(相同顏色的塊越大),壓縮檔就越小。

3.  為了確保轉換後的整數值不超過9 位,因此顏色排列表中的成員只能是256~511 ,又因為256 ,257 被保留為特殊標誌,實際上可用的只有258~511 共254個,如果超過,就要清除舊錶,重建立立(256是重新初始化顏色排列表的標誌,257是像素流結束的標誌)

4.  有一點必須說明,在轉換256色像素流時,與位元影像不同的是,它是從映像的左上方開始,遂行向下掃描的。如果是用API函數來獲得像素流時,必須把流重新排列。

 

GIF壓縮的特點:

    GIF壓縮前必須把映像轉換成256色以下的映像,因此映像的品質大打折扣,但是,我們從上面的壓縮結果來看,原映像像素流中的每一個值都能被準確地描敘、儲存下來。也就是說,在GIF解壓縮時,能準確無誤地還原出映像上的每個像素(如果原圖為256色以像時)。因此GIF壓縮也被稱為無損壓縮,這種壓縮能準確地體現圖形的清晰的邊緣,如果圖形是以文字掃描為主,GIF壓縮絕對是最佳的首選。

 

GIF檔案的組成:

GIF檔案的組成包含以下幾個部分:

 

版本  檔案頭  系統調色盤    [輔助資訊]   圖標題資訊   圖片壓縮資訊  結束標誌

 

(6bytes) (7bytes)               (8bytes)    (11bytes)    ((255+255+  +n )bytes) (2bytes)
 
 

 

 

 

 

 

 

版本:GIF89a 或 GIF87a

輔助資訊:只有在GIF透明顯示或GIF動畫檔案中才有。也是為了實現這些特性而設定的。

系統調色盤:位元影像和表徵圖檔案的調色盤中每個顏色是4個位元組,而GIF檔案中每個顏色是3個位元組。

圖片壓縮資訊:因為映像壓縮後資訊量的大小與映像本身的複雜程度有關,它不像位元影像一樣,只要知道映像的大小,就能計算出檔案大小來,而是要等到像素流全部壓縮完才能知道檔案的大小。因此,GIF檔案中的圖片壓縮資訊部分,是以254位元組大小為單元,向後申請的(每個單元再前面加上一個位元組表示本單元的長度,實際每單元共255個位元組),最後一單元是以實際大小來申請的。

結束標誌:以0  59 作為檔案結束標誌。

 

編程舉例:

為了實現GIF的壓縮,用VB6 建一個工程,表單名為“BnpToGif”, ScaleMode =3-Pixel;一個文字框用來輸入要開啟的檔案名稱或要儲存的檔案名稱;兩個按鈕;三個標籤;兩個圖片框,Picture2 用來顯示檔案壓縮的完成進度,Width :400 ,Height:13。Picture1 用來顯示開啟的位元影像,Visible=false ,AutoRedraw=true ,AutoSize=true ,ScaleMode =3-Pixel, BorderStyle=0-None。

“Save”按鈕初始時Enabled=false ,待按“Open”開啟Text1 輸入的位元影像檔案完成後,才變為可用狀態,單擊可以把原位元影像轉換成256色GIF檔案(自動更換尾碼名或另存新檔Text1中重新輸入的檔案名稱)

 
 

 

 

 

 

 

 

 

 

 

 

 

 

程式碼

Option Explicit

 

Private Type RGBTRIPLE  ’調色盤

    rgbRed As Byte

    rgbGreen As Byte

    rgbBlue As Byte

End Type

 

Private Type GifScreenDescriptor  ’檔案頭

    logical_screen_width As Integer  ’映像寬

    logical_screen_height As Integer  ’映像高

    Flags As Byte   ’檔案的品質(調色盤中顏色多少)的標誌

    background_color_index As Byte  ’儲存像素流第一個像素的值

    pixel_aspect_ratio As Byte  ’為0

End Type

’檔案頭中Flags 在256色時為231;128色時為198;64色時為165;32色時為132;16色時為227;8色時為194;4色時為161

 

 

Private Type GifImageDescriptor  ’圖片資訊頭

    ImageSeparator As Byte  ’為44

    Left As Integer  ’左

    Top As Integer  ’右

    Width As Integer  ’寬

    Height As Integer  ’高

Format As Byte  ’為0 

data As Byte  ’為每個像素占的位元

End Type

 

Private Type GifImageEnd  ’檔案結束標誌

    dat1 As Byte  ’為0

    dat2 As Byte  ’為59

End Type

 

Private Const GIF89a = "GIF89a"

 

Dim colTable As New Collection  '用於LZW壓縮時的顏色排列表

 

Dim bitpos, bitval As Byte   '用於位元運算時的位定位及轉化後的Byte值,並把該值寫入gfbyt(p255)中

Dim p255 As Long   '寫入檔案的總位元組數(定位)

Dim gfbyt() As Byte     '寫入檔案的位元組流

Dim buf() As Byte      '映像像素流(256色或以下)

Dim bpos(12) As Long   '用於位比較時的數1,2,4,8。。。。

Dim fname As String

 

'把gfbyt()分段(254個為一段)並寫入檔案

Private Sub putByte()

Dim i, k As Integer

Dim j As Long

k = Int(p255 / 254)

Dim sz() As Byte

ReDim sz(k)

sz(k) = p255 Mod 254

For i = k - 1 To 0 Step -1: sz(i) = 254: Next

 

k = 0

For j = 0 To p255

If j Mod 254 = 0 Then Put #1, , sz(k): k = k + 1

Put #1, , gfbyt(j)

Next

End Sub

 

'把壓縮後的表值寫入gfbyt()

'在256色時,表值為變長(9-12位)的Integer,256為清除舊錶,257為映像結束,表項為258—4095共3837個

'在4色時,表值為變長(3-12位)的Integer,4為清除舊錶,5為映像結束,表項為6—4095共4089個

Private Sub AddByte(ByVal bVal As Long, ByVal vLen As Byte)

Dim i As Byte

For i = 0 To vLen - 1

If (bVal And bpos(i)) Then bitval = bitval + bpos(bitpos)

bitpos = bitpos + 1

If bitpos = 8 Then

bitpos = 0

gfbyt(p255) = bitval

bitval = 0

p255 = p255 + 1

ReDim Preserve gfbyt(p255)   '再申請一個Byte並保留原來資料

gfbyt(p255) = 0

End If

Next

End Sub

 

Private Sub Command1_Click()

fname = Text1.Text

If Right(fname, 3) <> "bmp" Then Label3.Caption = "檔案不是 .bmp 檔案": Exit Sub

If Dir(fname) = "" Then Label3.Caption = "檔案不存在": Exit Sub

Picture1.Picture = LoadPicture(fname)

Command2.Enabled = True

End Sub

 

 

Private Sub Command2_Click()

'檢測儲存檔案名稱的正確性

fname = Text1.Text

Dim i, j As Integer

Dim pw, ph 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 Label3.Caption = "檔案路徑不存在": Exit Sub

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

 

    DoEvents

    jindu 10

   

'轉換映像為256色,並存入buf ()像素流中

  Dim r, g, b, rA, gA, bA, lIndex As Integer

  Dim col As Long

  Dim k, kk As Long

 

    ph = Picture1.Height - 1

    pw = Picture1.Width - 1

    kk = (ph + 1) * (pw + 1) - 1

    ReDim buf(kk) As Byte

    '轉換時使用的計算方法要和建立調色盤的演算法相一致

    For i = 0 To ph

    For j = 0 To pw

   

       col = Picture1.Point(j, i)

       If col = 16777215 Then

       buf(k) = 215

       Else

       b = Int(col / 65536)

       col = col - b * 65536

       g = Int(col / 256)

       r = col Mod 256

       rA = CInt(r / 51)

       gA = CInt(g / 51)

       bA = CInt(b / 51)

       buf(k) = bA * 36 + gA * 6 + rA

       End If

       k = k + 1

    Next

    Next

   

    DoEvents

    jindu 50

 

'寫入GIF檔案

Dim scr As GifScreenDescriptor

Dim im As GifImageDescriptor

Dim gifPalette(0 To 255) As RGBTRIPLE

Dim gend As GifImageEnd

Dim sPrefix As String

Dim sByte As String

Dim intCode As Integer

Dim nCount As Byte

   

’建立系統調色盤中的顏色,建立216個,前面轉換256色像素的計算方法要和這裡的建立方法相一致!

    For b = 0 To 255 Step 51

        For g = 0 To 255 Step 51

            For r = 0 To 255 Step 51

                gifPalette(lIndex).rgbBlue = b

                gifPalette(lIndex).rgbGreen = g

                gifPalette(lIndex).rgbRed = r

                lIndex = lIndex + 1

            Next

        Next

    Next

   

    scr.background_color_index = 215

    scr.Flags = 231

    scr.pixel_aspect_ratio = 0

    scr.logical_screen_width = Picture1.Width

    scr.logical_screen_height = Picture1.Height

   

    im.ImageSeparator = 44

    im.data = 8

    im.Format = 0

    im.Height = Picture1.Height

    im.Width = Picture1.Width

   

    '寫入GIF檔案頭

    Open fname For Binary As #1

   

    Put #1, , GIF89a

    Put #1, , scr

    Put #1, , gifPalette

    Put #1, , im

   

    '進行GIF壓縮,從 buf() 到 gfbyt()

    Dim l As Integer

    ReDim gfbyt(0)

    gfbyt(0) = 0

   

    l = 9

    AddByte 256, l

    sPrefix = Right$("00" & buf(0), 3)

    intCode = buf(0)

    r = 0

    For k = 1 To bufsize - 1

            sByte = Right$("00" & buf(k), 3)

            sPrefix = sPrefix & sByte

            On Error Resume Next

            intCode = colTable(sPrefix)

            If Err <> 0 Then

                nCount = colTable.Count

                If nCount = 3837 Then

                AddByte intCode, l

                Set colTable = Nothing

                Set colTable = New Collection

                AddByte 256, l

                l = 9

                sPrefix = sByte

                intCode = buf(k)

                GoTo 10

                End If

                colTable.Add nCount + 258, sPrefix

                If getlen(nCount + 257) > l Then l = l + 1 '根據需要增加位元組位元

                AddByte intCode, l

                sPrefix = sByte

                intCode = buf(k)

                Err.Clear

            End If

10:

    Next

    AddByte intCode, l  '把最後一個壓縮值寫入

   

    nCount = colTable.Count

    If nCount > 3837 Then AddByte 256, 9

    AddByte 257, 9   '寫入結束標誌

   

    putByte         '寫入檔案

   

  

    gend.dat1 = 0

    gend.dat2 = 59

    Put #1, , gend

    Close #1

    Erase buf

    Erase gfbyt

    Label3.Caption = "成功寫入" & fname & "檔案"

    jindu 100

End Sub

 

Private Sub jindu(ByVal vol As Integer) ’用於顯示進度

Picture2.Cls

Label2.Caption = vol & "   %"

Picture2.Line (0, 0)-Step(vol * 4, 13), , BF

DoEvents

End Sub

 

 

Private Function getlen(ByVal bVal As Long) As Byte

Select Case bVal

Case 0, 1

getlen = 1

Case Is < 4

getlen = 2

Case Is < 8

getlen = 3

Case Is < 16

getlen = 4

Case Is < 32

getlen = 5

Case Is < 64

getlen = 6

Case Is < 128

getlen = 7

Case Is < 256

getlen = 8

Case Is < 512

getlen = 9

Case Is < 1024

getlen = 10

Case Is < 2048

getlen = 11

Case Else

getlen = 12

End Select

 

End Function

 

Private Sub Form_Load()

Text1.Text = App.Path & "\yeye.bmp"

Dim i As Integer

For i = 0 To 12: bpos(i) = 2 ^ i: Next  '初始化用於位元運算的數

End Sub

 

 

編程舉例:  電子書格式(4色GIF):

系統調色盤中顏色的多少,直接影響著轉化後圖片的品質,同時也影響著壓縮檔的大小,如果圖片是以文字的掃描為主,我就可以用2色(黑和白)來進行壓縮,就能獲得最大的壓縮率且不影響圖片的閱讀。檔案頭中Flags=161 (4色),調色盤中4個顏色僅設定1個(255,255,255)即可,其餘3個不賦值為(0,0,0),在轉換映像像素流時,轉化為2色,這樣就能獲得最高的壓縮率。現在有許多電子書都使用這種格式(4色GIF)的壓縮。

我曾用一幅B4大小(1150*800像素),約1000個字的圖片,進行4色GIF壓縮,我們來看下面一組數字:24位位元影像約2630 K;256色GIF約45 K;4色GIF約24 K。由此可見,4色GIF壓縮可以獲得最大的壓縮比,約100 :1,而且映像品質非常好,用看圖工具放大數倍後,仍然清晰。這也是GIF成為獨步電子書王國的原因。下面我就再舉一個例子來展示4色GIF壓縮。

在前面的程式中添加一個Command3,編寫代碼:

 

Private Sub Command3_Click()

'檢測儲存檔案名稱的正確性

fname = Text1.Text

Dim i, j As Integer

Dim pw, ph 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 Label3.Caption = "檔案路徑不存在": Exit Sub

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

 

    DoEvents

    jindu 10

   

'轉換映像為256色,並存入buf ()像素流中

  Dim col As Long

  Dim k, kk As Long

 

    ph = Picture1.Height - 1

    pw = Picture1.Width - 1

    kk = (ph + 1) * (pw + 1) - 1

    ReDim buf(kk) As Byte

    '轉換時使用的計算方法要和建立調色盤的演算法相一致

    For i = 0 To ph

    For j = 0 To pw

   

       col = Picture1.Point(j, i)

       If col = 16777215 Then

       buf(k) = 1  '這裡只取兩色,黑和白

       Else

       buf(k) = 0

       End If

       k = k + 1

    Next

    Next

   

    DoEvents

    jindu 50

 

'寫入GIF檔案

Dim scr As GifScreenDescriptor

Dim im As GifImageDescriptor

Dim gifPalette(3) As RGBTRIPLE

Dim gend As GifImageEnd

Dim sPrefix As String

Dim sByte As String

Dim intCode As Long

Dim nCount As Long

   

   

                gifPalette(0).rgbBlue = 0

                gifPalette(0).rgbGreen = 0

                gifPalette(0).rgbRed = 0

                gifPalette(1).rgbBlue = 255

                gifPalette(1).rgbGreen = 255

                gifPalette(1).rgbRed = 255

   

    scr.background_color_index = 0

    scr.Flags = 161

    scr.pixel_aspect_ratio = 0

    scr.logical_screen_width = pw

    scr.logical_screen_height = ph

   

    im.ImageSeparator = 44

    im.data = 2

    im.Format = 0

    im.Height = ph

    im.Width = pw

   

   

    '寫入GIF檔案頭

    Open fname For Binary As #1

   

    Put #1, , GIF89a

    Put #1, , scr

    Put #1, , gifPalette

    Put #1, , im

   

    Dim l As Integer

    '進行GIF壓縮,從 buf() 到 gfbyt()

    ReDim gfbyt(0)

    gfbyt(0) = 0

   

    l = 3

    AddByte 4, l

    sPrefix = CStr(buf(0))

    intCode = buf(0)

   

    For k = 1 To bufsize - 1

            sByte = buf(k)

            sPrefix = sPrefix & sByte

            On Error Resume Next

            intCode = colTable(sPrefix)

            If Err <> 0 Then

                nCount = colTable.Count

                If nCount = 4089 Then  '表值滿9位,清除舊錶

                AddByte intCode, l

               

                '注意!重新顏色排列表時添加的標誌“4”要寫12位,而不是3位

                '即 000000000100

                AddByte 4, l

                Set colTable = Nothing

                Set colTable = New Collection

                l = 3

                nCount = 0

                sPrefix = buf(k)

                intCode = buf(k)

                GoTo 10

                End If

                colTable.Add nCount + 6, sPrefix

                If getlen(nCount + 5) > l Then l = l + 1 '根據需要增加位元組位元

                AddByte intCode, l

                sPrefix = sByte

                intCode = buf(k)

                Err.Clear

            End If

10:

    Next

    AddByte intCode, l  '把最後一個壓縮值寫入

   

    '寫入檔案

    AddByte 5, l

    putByte

   

    gend.dat1 = 0

    gend.dat2 = 59

    Put #1, , gend

    Close #1

    Erase buf

    Erase gfbyt

    Label3.Caption = "成功寫入" & fname & "檔案"

    jindu 100

End Sub

 

 

童躍   福建省華安縣際頭小學

tongyue2007@126.com

2007 年 5 月 1 日

 

本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/tongyue/archive/2007/07/30/1716572.aspx

 

http://zh.wikipedia.org/zh-tw/GIF

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.