轉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