在 .NET影像處理程式中時常必須用到SetPixel或GetPixel指令逐點繪圖或取得像素資訊,但是這兩個以像素為基礎的運算函數速度都很慢,尤其是在同樣的電腦中其他商業影像軟體的執行速度比我們的程式快上十倍時會讓玩家們非常氣餒。因為一定有些『密技』是我們還不知道的。老師我從這個MSDN範例開始研究,整理了一套VB的程式模組(Module),算是將此程式技巧包裝了一下,真的有快上10倍!
你可以下載程式模組拷貝它到你的專案,開啓專案後選擇功能表,專案→加入現有項目即可使用。如果不知道如何使用可以下載使用範例專案,裡面有範例程式告訴你如何使用快速與傳統模式的繪圖,也同時驗證它們在你的電腦中的執行時間,真的差很多!
它的基本原理是直接以數值陣列的方式處理圖像資料。影像當然是記憶體中的一段資料,但是使用SetPixl等等函數時考慮到影像的屬性,事實上會額外增加很多內部處理程序(包括影像座標與顏色資訊等等),如果可以避開這些程序,直接存取影像中屬於顏色部分的數值就會有高速的結果。
此程序主要動作包括:
1.『鎖定』點陣圖:取得影像在記憶體中的位置(指標),並通知作業系統不要隨便移動此記憶區塊的內容
說明:作業系統有權限不通知使用者自行管理記憶體
2.拷貝影像顏色資訊到一個位元組數值陣列(Byte Array)
3.直接用程式存取這個陣列(完成等同於SetPixel與GetPixel的動作)
4.拷貝數值陣列到影像的記憶體位置
5.解除鎖定
以程式碼的角度來說,事實上會多出許多行,但是執行速度因為不會讓.NET繪圖程模組程式碼
Module FastPixel
Dim rgb() As Byte '影像的可存取副本資料陣列
Dim bmpData As System.Drawing.Imaging.BitmapData
'影像資料
Dim ptr As IntPtr '影像資料所在的記憶體指標(位置)
Dim n As Integer '影像總共佔據的位元組數
Dim L As Integer
'一個影像列的記憶體位元組數,對於32位元電腦必須是32bit(或4byte)的倍數
Dim Bpx As Integer
'每一像素點是幾個位元組?通常為3(24bits)或4(32bits)
'鎖定點陣圖(Bitmap)物件的記憶體位置,建立一個可操作的為元組陣列副本
Function LockBMP(ByVal bmp As Bitmap) As Boolean
Try
Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height)
'矩形物件,定義影像範圍
bmpData = bmp.LockBits(rect, Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat) '鎖定影像區記憶體(暫時不接受作業系統的移動)
ptr = bmpData.Scan0 '影像區塊的記憶體指標
L = bmpData.Stride '每一影像列的長度(bytes)
Bpx = L \ bmp.Width '每一像素的位元組數(3或4)
n = L * bmp.Height '影像總位元組數
ReDim rgb(n - 1) '宣告影像副本資料陣列
System.Runtime.InteropServices.Marshal.Copy(ptr, rgb, 0, n)
'拷貝點陣圖資料到副本陣列
Return True
Catch ex As Exception
Return False
End Try
End Function
'複製位元組陣列副本的處理結果到Bitmap物件,並解除其記憶體鎖定
Function UnLockBMP(ByVal bmp As Bitmap) As Boolean
Try
System.Runtime.InteropServices.Marshal.Copy(rgb, 0, ptr, n)
'拷貝副本陣列資料到點陣圖位置
bmp.UnlockBits(bmpData) '解除鎖定
Return True
Catch ex As Exception
Return False
End Try
End Function
'在記憶體鎖定狀態下繪製顏色點(Argb)
Function SetP(ByVal i As Integer, ByVal j As Integer, ByVal r As Byte,
ByVal g As Byte, ByVal b As Byte, ByVal a As Integer) As Boolean
Try
Dim k As Integer = j * L + i * Bpx '索引位置計算
rgb(k) = b 'blue
rgb(k + 1) = g 'green
rgb(k + 2) = r 'red
If Bpx > 3 Then rgb(k + 3) = a '透明度
Return True
Catch ex As Exception
Return False
End Try
End Function
'在記憶體鎖定狀態下繪製顏色點(rgb)
Function SetP(ByVal i As Integer, ByVal j As Integer, ByVal r As Byte,
ByVal g As Byte, ByVal b As Byte) As Boolean
Try
Dim k As Integer = j * L + i * Bpx '索引位置計算
rgb(k) = b 'blue
rgb(k + 1) = g 'green
rgb(k + 2) = r 'red
If Bpx > 3 Then rgb(k + 3) = 255 '透明度
Return True
Catch ex As Exception
Return False
End Try
End Function
'在記憶體鎖定狀態下取得顏色點(Pixel)
Function GetP(ByVal i As Integer, ByVal j As Integer) As Color
Dim k As Integer = j * L + i * Bpx '索引位置計算
Dim C As Color = Color.FromArgb(rgb(k + 3), rgb(k + 2), rgb(k + 1),
rgb(k))
Return C
End Function
'在記憶體鎖定狀態下取得紅色值(byte)
Function GetR(ByVal i As Integer, ByVal j As Integer) As Byte
Return rgb(j * L + i * Bpx + 2)
End Function
'在記憶體鎖定狀態下取得綠色值(byte)
Function GetG(ByVal i As Integer, ByVal j As Integer) As Byte
Return rgb(j * L + i * Bpx + 1)
End Function
'在記憶體鎖定狀態下取得藍色值(byte)
Function GetB(ByVal i As Integer, ByVal j As Integer) As Byte
Return rgb(j * L + i * Bpx)
End Function
End Module
|
範例程式算
rgb(k) = b 'blue
rgb(k + 1) = g 'green
rgb(k + 2) = r 'red
If Bpx > 3 Then rgb(k + 3) = 255 '透明度
Return True
Catch ex As Exception
Return False
End Try
End Function
'在記憶體鎖定狀態下取得顏色點(Pixel)
Function GetP(ByVal i As Integer, ByVal j As Integer) As Color
Dim k As Integer = j * L + i * Bpx '索引位置計算
Dim C As Color = Color.FromArgb(rgb(k + 3), rgb(k + 2), rgb(k + 1),
rgb(k))
Return C
End Function
'在記憶體鎖定狀態下取得紅色值(byte)
Function GetR(ByVal i As Integer, ByVal j As Integer) As Byte
Return rgb(j * L + i * Bpx + 2)
End Function
'在記憶體鎖定狀態下取得綠色值(byte)
Function GetG(ByVal i As Integer, ByVal j As Integer) As Byte
Return rgb(j * L + i * Bpx + 1)
End Function
'在記憶體鎖定狀態下取得藍色值(byte)
Function GetB(ByVal i As Integer, ByVal j As Integer) As Byte
Return rgb(j * L + i * Bpx)
End Function
End Module |