VB圖像處理之二次線性插值的應用
上次講到了用DIB方法來獲取圖像的像素。從這次開始將如果運用已經得到的像素來處理圖像。
圖像插值放大的方法有很多,最主要的有二次線性插值和三次線性插值這兩種。這次我把自己的程序中所用的二次線性插值的算法公布給大家,希望對各位要使用VB寫類似程序的朋友有所幫助。
程序中用到的API、數據類型、全局變量的定義請參考上一篇:《VB實現圖像在數據庫的存儲與顯示》
全局變量定義:
簡單解釋一下關于二次線性插值算法。
(為了說明算法本身,我們只計算這個圖片的紅色分量,因為紅綠藍三種顏色的計算方法完全相同)
假設我們有一個很簡單的圖片,圖片只有4個像素(2*2)
現在我們要把這個圖片插值到9個像素:3*3
其中大寫的字母代表原來的像素,小寫字母代表插值得到的新像素。
想必看到這個圖,大家心里已經有了這個算法了。
推導:
很簡單,對吧,先從一個方向把只涉及兩個原始像素的新像素算出來。我們這里假定先計算水平方向。而在算垂直方向的插值的時候,因為ab和cd已經在前面算好了,所以abcd的計算也和計算ac和bd沒有任何區別了。
有可能為有朋友已經想到把原來的圖像插值到4*4或5*5的方法了。
推導:
以A和B為例,先求出原始像素的差(A-B)再算出每一步的遞增量(A-B) / 3;然后每一個新的點就是在前面那個點的值加上這個遞增量就是了。
這里我們假設A=100, B=255 放大倍率為3,水平方向插值;先計算出原始像素的差:(B-A) = 255-100 =155
再計算出水平方向每一步的遞增量:(A-B) / 3=155 / 3 = 51.7
這里我們用一個變量DRt來記錄這個遞增量(這里只用紅色來做例子)
好了,其實二次線性算法就是這么一個東西,并不復雜。或許有寫朋友會對于我給出的代碼產生疑問。很簡單的一個算法為什么要寫這么多代碼。
其實答案很簡單:為了提高速度。
在VB中“+”和“-”永遠是最快的,“*”要比“/”和“”快。不論是什么類型的變量都是這樣的。
下面再來分析一下我的程序。
在我的程序中把兩個方向的插值分解成了兩個單獨的部分。
先把
變成:
再變成:
這兩個方向的插值算法完全相同
而Xratio 和Yratio 這兩個變量則用來記錄水平方向和垂直方向的放大倍率。所以這個過程也能夠讓圖像縮放不按照原始的縱橫比進行。
好了,將這個模塊和全局變量添加到上次建立的工程模塊中。
把按鈕中的代碼改成:
圖像是否已經放大到原來的兩倍了呢?速度不算很慢吧?
什么?很慢?先編譯成EXE再運行吧。下面是效果圖:
原圖:
二次線性插值放大5倍:
關于二次線性插值就說到這里了,下一次將說一些基本的濾鏡效果:銳化、柔化、擴散、雕刻。請大家繼續關注
圖像插值放大的方法有很多,最主要的有二次線性插值和三次線性插值這兩種。這次我把自己的程序中所用的二次線性插值的算法公布給大家,希望對各位要使用VB寫類似程序的朋友有所幫助。
程序中用到的API、數據類型、全局變量的定義請參考上一篇:《VB實現圖像在數據庫的存儲與顯示》
| Public Sub ZoomImage(ByVal OutPutWidth As Long, ByVal OutputHeight As Long) Dim I As Long Dim L As Long Dim X As Long Dim Y As Long Dim Xb As Long Dim Yb As Long Dim Xe As Long Dim Ye As Long Dim M As Integer Dim N As Integer Dim CurR As Long Dim CurG As Long Dim CurB As Long Dim NxtR As Integer Dim NxtG As Integer Dim NxtB As Integer Dim DR As Single Dim DG As Single Dim DB As Single Dim DRt As Single Dim DGt As Single Dim DBt As Single Dim Xratio As Single Dim Yratio As Single Dim CurStep As Single Dim NxtStep As Single Dim NegN As Single On Error GoTo ErrLine If Not CanZoom Then Exit Sub Done = False OutPutWid = OutPutWidth - 1 OutPutHei = OutputHeight - 1 I = (Bits 8) - 1 ReDim ColTmp(I, InPutWid, OutPutHei) '先從Y方向進行縮放處理,結果保存在此中間數組內 ReDim ColOut(I, OutPutWid, OutPutHei) Xratio = OutPutWid / InPutWid Yratio = OutPutHei / InPutHei TimeZoom = timeGetTime NegN = 1 / Int(Yratio + 1) For X = 0 To InPutWid CurR = ColVal(0, X, 0) CurG = ColVal(1, X, 0) CurB = ColVal(2, X, 0) CurStep = 0 NxtStep = 0 For Y = 0 To InPutHei - 1 NxtStep = CurStep + Yratio Yb = CurStep Ye = NxtStep N = Ye - Yb ColTmp(0, X, Yb) = CurR ColTmp(1, X, Yb) = CurG ColTmp(2, X, Yb) = CurB M = Y + 1 NxtR = ColVal(0, X, M) NxtG = ColVal(1, X, M) NxtB = ColVal(2, X, M) If N > 1 Then DRt = (NxtR - CurR) * NegN DGt = (NxtG - CurG) * NegN DBt = (NxtB - CurB) * NegN DR = 0 DG = 0 DB = 0 For L = Yb + 1 To Ye - 1 DR = DR + DRt DG = DG + DGt DB = DB + DBt ColTmp(0, X, L) = CurR + DR ColTmp(1, X, L) = CurG + DG ColTmp(2, X, L) = CurB + DB Next End If CurStep = NxtStep CurR = NxtR CurG = NxtG CurB = NxtB Next ColTmp(0, X, OutPutHei) = NxtR ColTmp(1, X, OutPutHei) = NxtG ColTmp(2, X, OutPutHei) = NxtB Next NegN = 1 / Int(Xratio + 1) For Y = 0 To OutPutHei CurR = ColTmp(0, 0, Y) CurG = ColTmp(1, 0, Y) CurB = ColTmp(2, 0, Y) CurStep = 0 NxtStep = 0 For X = 0 To InPutWid - 1 NxtStep = CurStep + Xratio Xb = CurStep Xe = NxtStep N = Xe - Xb ColOut(0, Xb, Y) = CurR ColOut(1, Xb, Y) = CurG ColOut(2, Xb, Y) = CurB M = X + 1 NxtR = ColTmp(0, M, Y) NxtG = ColTmp(1, M, Y) NxtB = ColTmp(2, M, Y) If N > 1 Then DRt = (NxtR - CurR) * NegN DGt = (NxtG - CurG) * NegN DBt = (NxtB - CurB) * NegN DR = 0 DG = 0 DB = 0 For L = Xb + 1 To Xe - 1 DR = DR + DRt DG = DG + DGt DB = DB + DBt ColOut(0, L, Y) = CurR + DR ColOut(1, L, Y) = CurG + DG ColOut(2, L, Y) = CurB + DB Next End If CurStep = NxtStep CurR = NxtR CurG = NxtG CurB = NxtB Next ColOut(0, OutPutWid, Y) = NxtR ColOut(1, OutPutWid, Y) = NxtG ColOut(2, OutPutWid, Y) = NxtB Next Done = True TimeZoom = timeGetTime - TimeZoom CanPut = True Exit Sub ErrLine: MsgBox Err.Description End Sub |
全局變量定義:
| Dim ColTmp() As Byte '用于保存插值中間變量 Dim OutPutHei As Long '要插值的目標高度 Dim OutPutWid As Long '要插值的目標寬度 Public TimeZoom As Long '插值運算使用的時間 |
簡單解釋一下關于二次線性插值算法。
(為了說明算法本身,我們只計算這個圖片的紅色分量,因為紅綠藍三種顏色的計算方法完全相同)
假設我們有一個很簡單的圖片,圖片只有4個像素(2*2)
| A B C D |
現在我們要把這個圖片插值到9個像素:3*3
| A ab B ac abcd bd C cd D |
其中大寫的字母代表原來的像素,小寫字母代表插值得到的新像素。
想必看到這個圖,大家心里已經有了這個算法了。
| ab=(A+B) / 2 cd=(C+D) / 2 ac=(A+C) / 2 bd=(B+D) / 2 abcd=(ab+cd) / 2=(A+B+C+D) / 4 |
推導:
| ab= A + (B-A) / 2 cd=C +(D-C) / 2 ... |
很簡單,對吧,先從一個方向把只涉及兩個原始像素的新像素算出來。我們這里假定先計算水平方向。而在算垂直方向的插值的時候,因為ab和cd已經在前面算好了,所以abcd的計算也和計算ac和bd沒有任何區別了。
有可能為有朋友已經想到把原來的圖像插值到4*4或5*5的方法了。
| A ab1 ab2 B ac1 ab1cd11 ab2cd21 bd1 ac2 ab1cd12 ab2cd22 bd2 C cd1 cd2 D |
推導:
| ab1 = A + (B-A) * 1 / 3 ab2 = A + (B-A) * 2 / 3 =ab1+(B-A) / 3 cd1 = C + (D-C) * 1 / 3 cd1 = C + (D-C) * 2 / 3 =cd1+(D-C) / 3 ... |
以A和B為例,先求出原始像素的差(A-B)再算出每一步的遞增量(A-B) / 3;然后每一個新的點就是在前面那個點的值加上這個遞增量就是了。
這里我們假設A=100, B=255 放大倍率為3,水平方向插值;先計算出原始像素的差:(B-A) = 255-100 =155
再計算出水平方向每一步的遞增量:(A-B) / 3=155 / 3 = 51.7
這里我們用一個變量DRt來記錄這個遞增量(這里只用紅色來做例子)
| ab1 = A + DRt = 100+51.7 =151 ab2 = ab1 + DRt = 151+51.7 = 202 |
好了,其實二次線性算法就是這么一個東西,并不復雜。或許有寫朋友會對于我給出的代碼產生疑問。很簡單的一個算法為什么要寫這么多代碼。
其實答案很簡單:為了提高速度。
在VB中“+”和“-”永遠是最快的,“*”要比“/”和“”快。不論是什么類型的變量都是這樣的。
下面再來分析一下我的程序。
在我的程序中把兩個方向的插值分解成了兩個單獨的部分。
先把
| A B C D |
變成:
| A ab1...abN B C cd1...cdN D |
再變成:
| A ab1...abN B ac1 ............. db1 ... ............ ... acN .............. bdN C cd1...cdN D |
這兩個方向的插值算法完全相同
而Xratio 和Yratio 這兩個變量則用來記錄水平方向和垂直方向的放大倍率。所以這個過程也能夠讓圖像縮放不按照原始的縱橫比進行。
好了,將這個模塊和全局變量添加到上次建立的工程模塊中。
把按鈕中的代碼改成:
| sub command1_click() With picture1 .ScaleMode=3 .BorderStyle=0 DibGet .hdc ,0 , 0 , .scalewidth , .scaleheight ZoomImage , .scalewidth * 2 , .scaleheight * 2 End With picture2.AutoRedraw=True DibPut picture2.hdc picture2.refresh end sub |
圖像是否已經放大到原來的兩倍了呢?速度不算很慢吧?
什么?很慢?先編譯成EXE再運行吧。下面是效果圖:
原圖:
二次線性插值放大5倍:
![]() |
關于二次線性插值就說到這里了,下一次將說一些基本的濾鏡效果:銳化、柔化、擴散、雕刻。請大家繼續關注
