用VB寫高效的影像處理程式 (轉)
自盤古開天地以來(好像誇張了點),一直有人抱怨VB速度慢。特別是,被認為是VB的禁區。說起來也是,市面上的關於VB的影像處理的資料都是先講計算公式,再直接用PSet(或SetPixel)逐點畫(至少我見過的書都是這樣)。效果是辦到了,但速度慢得離譜:對一幅640*480的影像進行半透明合併就需要10秒鐘;而在PhotoShop中,只要一設定圖層的透明度,半透明效果立即呈現。難怪有人說VB的閒話。 但這並不表示VB不能寫高速的影像處理程式,速度慢是因為沒有使用正確的方法。 從VB5開始,能以本機程式碼編譯成exe,所以不存在程式碼速度的問題。那麼,是什麼拖慢了速度呢?就是PSet和SetPixel!PSet把浮點形式的座標轉為畫素單位,再交給SetPixel處理。而SetPixel呢,座標系轉化、剪裁區域判斷、將顏色匹配為裝置支援的最接近的,最後還要根據不同的顏色格式定址、為將顏色寫入其所在位進行位運算。經過這麼多層處理,速度不慢才怪。 那麼,怎樣才能提高處理速度呢?使用DIB,直接對點陣圖所在進行操作,速度可以大大提高。現在看看/Zyl910/ImgTest.zip">範例程式,這只是一個簡單的色彩演示程式。
1.VC比VB_DIB、VB_DIB_Ptr快兩倍,這是因為SafeArray結構的陣列比真正的指標慢,但也不是某些人所說的70~100倍;
2.VB_DIB_Ptr比VB_DIB慢一點,這是因為模擬指標本來就是靠SafeArray結構的陣列,而且模擬指標需要對兩個陣列進行操作,所以速度慢一點;
3.真正差了70~100倍是VB_PSet和VB_SetPixel,特別是VB_PSet在Windows98下與VB_DIB差了145倍。
以上可證,速度慢的原因是SetPixel非常低效,而並不是VB的問題。雖然VC的的確比較快,但是我寫這篇文章不是為了討論速度極限(否則這篇文章會改名為《如何用寫高速的影像處理程式》),而是為了告訴大家如何在VB中寫能夠實時處理的影像處理程式。
在 Windows 3.0 以前,Windows用的是D(裝置有關點陣圖)。DDB沒有調色盤,顯示的顏色依賴,處理色彩很不方便。所以 在 Windows 3.0中 重新定義了BMP檔案格式(BMP 3.0),使其支援裝置無關點陣圖——也就是DIB。 時至今日,BMP的版本號已升至5.0( 4.0、Windows95 定義了 BMP 4.0,、 定義了 BMP 5.0),但基本結構沒有變——仍是 BMP檔案頭 和 DIB 組成: BMP檔案 BITMAPFILEHEADER BMP檔案頭 DIB BITMAPINFOHEADER 點陣圖資訊頭 BITMAPINFO RGBQUAD[] 調色盤 點陣圖資料
(#代表可以不填(=0)的專案)
typedef struct tagBITMAPFILEHEADER { // bmfh bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER;
VB宣告:Type BITMAPFILEHEADER bfType(0 to 1) As Byte bfSize As Long bfReserved1 As Integer bfReserved2 As Integer bfOffBits As Long End Type
說明: bfType 指示檔案的型別,必須是“BM” bfSize# 指示檔案的大小,包括BITMAPFILEHEADER bfReserved1 保留,=0 bfReserved2 保留,=0 bfOffBits# 從檔案頭到點陣圖資料的偏移位元組數typedef struct tagBITMAPINFOHEADER{ // bmih DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER;
VB宣告:Type BITMAPINFOHEADER 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
說明: biSize BITMAPINFOHEADER結構的大小。BMP有多個版本,就靠biSize來區別:B.0:BITMAPINFOHEADER(=40)
B.0:BITMAPV4HEADER(=108)
BMP5.0:BITMAPV5HEADER(=124) biWidth 點陣圖的高度,單位是畫素 biHeight 點陣圖的寬度,單位是畫素 biPlanes 裝置的位平面數。現在都是1 biBitCount 影像的顏色位數
0:當biCompression=BI_JPEG時必須為0(BMP 5.0)
1:單色點陣圖
4:16色點陣圖
8:256色點陣圖
16:增強色點陣圖,預設為555格式
24:真彩色點陣圖
32:32位點陣圖,預設情況下Windows不會處理最高8位,可以將它作為自己的Alpha通道 biCompression 方式
BI_RGB:無壓縮
BI_RLE8:行程編碼壓縮,biBitCount必須等於8
BI_RLE4:行程編碼壓縮,biBitCount必須等於4
BI_BITFIELDS:指定RGB掩碼,biBitCount必須等於16、32
BI_JPEG:JPEG壓縮(BMP 5.0)
BI_PNG:PNG壓縮(BMP 5.0) biSizeImage# 實際的點陣圖資料所佔位元組(biCompression=BI_RGB時可以省略) biXPelsPerMeter# 目標裝置的水平解析度,單位是每米的畫素個數 biYPelsPerMeter# 目標裝置的垂直解析度,單位是每米的畫素個數 biClrUsed# 使用的顏色數(當biBitCount等於1、4、8時才有效)。如果該項為0,表示顏色數為2^biBitCount biClrImportant# 重要的顏色數。如果該項為0,表示所有顏色都是重要的
只有biBitCount等於1、4、8時才有調色盤。調色盤實際上是一個陣列,元素的個數由biBitCount和biClrUsed決定。
typedef struct tagRGBQUAD { // rgbq BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; } RGBQUAD;
VB宣告:Private Type RGBQUAD rgbBlue As Byte rgbGreen As Byte rgbRed As Byte rgbReserved As Byte End Type
說明: rgbBlue 藍色分量 rgbGreen 綠色分量 rgbRed 紅色分量 rgbReserved# 保留,=0一行的影像資料叫做一個掃描行。一個掃描行的長度必須是4的倍數(位元組),如果不是,則需要補齊。計算公式:LineBytes=((biWidth*biBitCount+31)And &HFFFFFFE0)8
由於BMP設定者認為數學座標系更總要,所以DIB的掃描行是逆序的(相對於螢幕座標系而言),即螢幕上的第一行是DIB點陣圖資料的最後一行。 ◆1位色:
用1位表示一個畫素,所以一個位元組可以表示8個畫素。座標是從最左邊(最高位)開始的,而不是一般情況下的最低位。在記憶體的擺放形式如下: 位元組 0 ... 位 7 6 5 4 3 2 1 0 畫素 0 1 2 3 4 5 6 7 ◆4位色:
用4位表示一個畫素,所以一個位元組可以表示2個畫素。座標是從最左邊(最高位)開始的,而不是一般情況下的最低位。在記憶體的擺放形式如下: 位元組 0 ... 位 7 6 5 4 3 2 1 0 畫素 0 1 畫素位 3 2 1 0 3 2 1 0 ◆8位色:
用8位表示一個畫素,所以一個位元組剛好只能表示一個畫素。在記憶體的擺放形式如下: 位元組 0 1 ... 畫素 0 1 ◆16位色:
用16位表示一個畫素,所以兩個位元組可以表示1個畫素。預設情況下16位DIB是555格式,最高位無效(這對VB是個福音,因為VB沒有16位無符號型)。在記憶體的擺放形式如下(PC機是低位元組在前): 位元組 0 1 2 3 ... 位 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 畫素 0 1 RGB G B x R G G B x R G RGB位 2 1 0 4 3 2 1 0 0 4 3 2 1 0 4 3 2 1 0 4 3 2 1 0 0 4 3 2 1 0 4 3 ◆24位色:
用24位表示一個畫素,所以三個位元組可以表示1個畫素。注意它的順序是BGR,而不是傳統的RGB。在記憶體的擺放形式如下: 位元組 0 1 2 3 4 5 ... 畫素 0 1 RGB B G R B G R ◆32位色:
用32位表示一個畫素,所以四個位元組可以表示1個畫素。注意絕大多數的GDI函式不會處理Alpha通道(只有AlphaBlend支援)。在記憶體的擺放形式如下: 位元組 0 1 2 3 4 5 6 7 ... 畫素 0 1 RGB B G R A B G R A
int SetDIBitsToDevice( HDC hDC, // handle to device context int XDest, // x-coordinate of upper-left corner of dest. rect. int YDest, // y-coordinate of upper-left corner of dest. rect. DWORD dwWidth, // rectangle width DWORD dwHeight, // source rectangle height int XSrc, // x-coordinate of lower-left corner of source rect. int YSrc, // y-coordinate of lower-left corner of source rect. UINT uStartScan, // first scan line in array UINT cScanLines, // number of scan lines CONST VOID *lpvBits, // address of array with DIB bits CONST BITMAPINFO *lpbmi, // address of structure with bitmap info. UINT fuColorUse // RGB or palette indexes );
VB宣告: Declare Function SetDIBitsToDevice Lib "gdi32.dll" (ByVal hDC As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal dwWidth As Long, ByVal dwHeight As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As Any, lpbmi As Any, ByVal fuColorUse As Long) As Long 說明: 將一幅與裝置無關點陣圖的全部或部分資料直接複製到一個裝置。這個函式在裝置中定義了一個目標矩形,以便接收點陣圖資料。它也在DIB中定義了一個源矩形,以便從中提取資料 返回值: 如函式執行成功,返回欲複製的掃描線的數量;如返回常數GDI_ERROR,表示出錯 引數: hDC 一個裝置場景的控制程式碼。該場景用於接收點陣圖資料 XDest 指定繪製區域的左上角X座標 YDest 指定繪製區域的左上角Y座標 dwWidth 指定繪製區域的高度 dwHeight 指定繪製區域的寬度 XSrc 矩形在DIB中的起點X座標 YSrc 矩形在DIB中的起點Y座標 uStartScan lpvBits中第一條掃描線的編號。如lpbmi之BITMAPINFOHEADER部分的biHeight欄位是正數,那麼這條掃描線就會從點陣圖的底部開始計算;如果是負數,就從頂部開始計算 cScanLines 欲複製的掃描線數量 lpvBits 指向一個緩衝區的指標。這個緩衝區包含了以DIB格式描述的點陣圖資料;這種格式是由lpbmi指定的 lpbmi 指向BITMAPINFO(為相容BMP4/5而宣告成Any),對DIB的格式和顏色進行描述的一個結構 fuColorUse DIB_PAL_COLORS 顏色表是一個整數陣列,其中包含了與目前選入hDC裝置場景的調色盤相關的 DIB_RGB_COLORS 顏色表包含了RGB顏色int StretchDIBits( HDC hDC, // handle to device context int XDest, // x-coordinate of upper-left corner of dest. rectangle int YDest, // y-coordinate of upper-left corner of dest. rectangle int nDestWidth, // width of destination rectangle int nDestHeight, // height of destination rectangle int XSrc, // x-coordinate of upper-left corner of source rectangle int YSrc, // y-coordinate of upper-left corner of source rectangle int nSrcWidth, // width of source rectangle int nSrcHeight, // height of source rectangle CONST VOID *lpBits, // address of bitmap bits CONST BITMAPINFO *lpBitsInfo, // address of bitmap data UINT iUsage, // usage flags DWORD dwRop // raster operation code );
VB宣告: Declare Function StretchDIBits Lib "gdi32" (ByVal hDC As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal nDestWidth As Long, ByVal nDestHeight As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, lpBits As Any, lpBitsInfo As Any, ByVal wUsage As Long, ByVal dwRop As Long) As Long 說明: 根據一幅與裝置無關的點陣圖建立一幅與裝置有關的點陣圖 返回值: 執行成功返回點陣圖控制程式碼,零表示失敗 引數: hDC 一個裝置場景的控制程式碼,該裝置場景定義了要建立的與裝置有關點陣圖的資訊 XDest 指定繪製區域的左上角X座標 YDest 指定繪製區域的左上角Y座標 nDestWidth 指定繪製區域的高度 nDestHeight 指定繪製區域的寬度 XSrc 矩形在DIB中的起點X座標 YSrc 矩形在DIB中的起點Y座標 nSrcWidth 指定原點陣圖繪製區域的左上角X座標 nSrcHeight 指定原點陣圖繪製區域的左上角Y座標 lpBits 指向一個緩衝區的指標。這個緩衝區包含了以DIB格式描述的點陣圖資料;這種格式是由lpBitsInfo指定的 lpBitsInfo 指向BITMAPINFO(為相容BMP4/5而宣告成Any),對DIB的格式和顏色進行描述的一個結構 iUsage DIB_PAL_COLORS 顏色表是一個整數陣列,其中包含了與目前選入hDC裝置場景的調色盤相關的索引 DIB_RGB_COLORS 顏色表包含了RGB顏色 dwRop 欲進行的光柵運算HBITMAP CreateDIBitmap( HDC hDC, // handle to device context CONST BITMAPINFOHEADER *lpbmih, // pointer to bitmap size and format data DWORD fdwInit, // initialization flag CONST VOID *lpbInit, // pointer to initialization data CONST BITMAPINFO *lpbmi, // pointer to bitmap color-format data UINT fuUsage // color-data usage );
VB宣告: Declare Function CreateDIBitmap Lib "gdi32" (ByVal hDC As Long, lpbmih As Any, ByVal fdwInit As Long, lpbInit As Any, lpbmi As Any, ByVal fuUsage As Long) As Long 說明: 將一幅與裝置無關點陣圖的全部或部分資料直接複製到一個裝置。這個函式在裝置中定義了一個目標矩形,以便接收點陣圖資料。它也在DIB中定義了一個源矩形,以便從中提取資料 返回值: 執行成功則返回掃描線的數量,零表示失敗。會設定GetLastError 引數: hDC 一個裝置場景的控制程式碼。該場景用於接收點陣圖資料 lpbmih BITMAPINFOHEADER(為相容BMP4/5而宣告成Any),對DIB的格式進行描述的一個結構 fdwInit 如不應對點陣圖資料進行初始化,那麼設為零。如設為CBM_INIT,表示根據lpbInit和 lpbmi引數對點陣圖進行初始化 lpbInit 指向一個緩衝區的指標。這個緩衝區包含了以DIB格式描述的點陣圖資料;這種格式是由lpbmi指定的 lpbmi 指向BITMAPINFO(為相容BMP4/5而宣告成Any),對DIB的格式和顏色進行描述的一個結構 fuUsage DIB_PAL_COLORS 顏色表是一個整數陣列,其中包含了與目前選入hDC裝置場景的調色盤相關的索引 DIB_RGB_COLORS 顏色表包含了RGB顏色HBITMAP CreateDIBSection( HDC hDC, // handle to device context CONST BITMAPINFO *lpbmi, // pointer to structure containing bitmap size, format, and color data UINT iUsage, // color data type indicator: RGB values or palette indexes VOID *ppvBits, // pointer to variable to receive a pointer to the bitmap's bit values HANDLE hSection, // optional handle to a file map DWORD dwOffset // offset to the bitmap bit values within the file mapping object );
VB宣告: Declare Function CreateDIBSection Lib "gdi32" (ByVal hDC As Long, lpbmi As Any, ByVal iUsage As Long, ByRef ppvBits As Long, ByVal hSection As Long, ByVal dwOffset As Long) As Long 說明: CreateDIBSection能建立一種特殊的DIB,稱為DIB項(DIBSection),然後返回一個GDI點陣圖的控制程式碼。它提供了DIB和GDI點陣圖的最好的特性。這樣我們可以直接訪問DIB的記憶體,可以利用點陣圖控制程式碼和記憶體裝置環境,我們甚至還可以在DIB中GDI函式來繪圖 返回值: 執行成功返回DIBSection點陣圖的控制程式碼,零表示失敗。會設定GetLastError 引數: hDC 一個裝置場景的控制程式碼。如dw設為DIB_PAL_COLORS,那麼DIB顏色表就會用來自邏輯調色盤的顏色進行初始化 lpbmi 指向BITMAPINFO(為相容BMP4/5而宣告成Any),這個結構初始化成欲建立的那幅點陣圖的配置資料 iUsage DIB_PAL_COLORS 顏色表是一個整數陣列,其中包含了與目前選入hDC裝置場景的調色盤相關的索引 DIB_RGB_COLORS 顏色表包含了RGB顏色 ppvBits 用於得到DIBSection資料區的記憶體地址 hSection 指向一個檔案對映的可選控制程式碼,點陣圖將在其中建立。如設為零,Windows會自動分配記憶體 dwOffset 如指定了控制程式碼,就用這個引數指定點陣圖資料在檔案對映物件中的偏移量int GetDIBits( HDC hDC, // handle to device context HBITMAP hbmp, // handle to bitmap UINT uStartScan, // first scan line to set in destination bitmap UINT cScanLines, // number of scan lines to copy LPVOID lpvBits, // address of array for bitmap bits LPBITMAPINFO lpbmi,// address of structure with bitmap data UINT uUsage // RGB or palette index );
VB宣告: Declare Function GetDIBits Lib "gdi32" (ByVal hDC As Long, ByVal hbmp As Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As Any, lpbmi As Any, ByVal uUsage As Long) As Long 說明: 該函式利用申請到的記憶體,由GDI點陣圖得到DIB點陣圖資料。透過該函式,可以對DIB的格式進行控制,可以制定顏色的位數,而且可以指定是否進行壓縮。如果採用了壓縮方式,則必須呼叫該函式兩次,一次為了得到所需記憶體,另外一次為了得到點陣圖資料 返回值: 執行成功則返回掃描線的數量,零表示失敗。會設定GetLastError 引數: hDC 定義了與裝置有關點陣圖hBitmap的配置資訊的一個裝置場景的控制程式碼 hbmp 源點陣圖的控制程式碼 uStartScan 欲複製到DIB中的第一條掃描線的編號 cScanLines 欲複製的掃描線數量 lpvBits 指向一個緩衝區的指標。這個緩衝區包含了以DIB格式描述的點陣圖資料;這種格式是由lpbmi指定的 lpbmi 指向BITMAPINFO(為相容BMP4/5而宣告成Any).對DIB的格式及顏色進行說明的一個結構。在BITMAPINFOHEADER結構中,從biSize到biCompression之間的所有欄位都必須初始化 uUsage DIB_PAL_COLORS 顏色表是一個整數陣列,其中包含了與目前選入hDC裝置場景的調色盤相關的索引 DIB_RGB_COLORS 顏色表包含了RGB顏色int SetDIBits( HDC hDC, // handle to device context HBITMAP hbmp, // handle to bitmap UINT uStartScan, // starting scan line UINT cScanLines, // number of scan lines CONST VOID *lpvBits, // array of bitmap bits CONST BITMAPINFO *lpbmi, // address of structure with bitmap data UINT uUsage // type of color indexes to use );
VB宣告: Declare Function SetDIBits Lib "gdi32" (ByVal hDC As Long, ByVal hbmp As Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As Any, lpbmi As Any, ByVal uUsage As Long) As Long 說明: 將來自與裝置無關點陣圖的二進位制位複製到一幅與裝置有關的點陣圖裡 返回值: 執行成功則返回掃描線的數量,零表示失敗。會設定GetLastError 引數: hDC 定義了與裝置有關點陣圖hBitmap的配置資訊的一個裝置場景的控制程式碼 hbmp 源點陣圖的控制程式碼 uStartScan 欲複製到DIB中的第一條掃描線的編號 cScanLines 欲複製的掃描線數量 lpvBits 指向一個緩衝區的指標。這個緩衝區包含了以DIB格式描述的點陣圖資料;這種格式是由lpbmi指定的 lpbmi 指向BITMAPINFO(為相容BMP4/5而宣告成Any).對DIB的格式及顏色進行說明的一個結構。在BITMAPINFOHEADER結構中,從biSize到biCompression之間的所有欄位都必須初始化 uUsage DIB_PAL_COLORS 顏色表是一個整數陣列,其中包含了與目前選入hDC裝置場景的調色盤相關的索引 DIB_RGB_COLORS 顏色表包含了RGB顏色UINT GetDIBColorTable( HDC hDC, // handle to device context whose DIB is of interest UINT uStartIndex, // color table index of first entry to retrieve UINT cEntries, // number of color table entries to retrieve RGBQUAD *pColors // pointer to buffer that receives color table entries );
VB宣告: Declare Function GetDIBColorTable Lib "gdi32" (ByVal hDC As Long, ByVal uStartIndex As Long, ByVal cEntries As Long, pColors As RGBQUAD) As Long 說明: 從選入裝置場景的DIBSection中取得顏色表資訊 返回值: 取回的顏色條目數量,零表示失敗。會設定GetLastError 引數: hDC 已選入了一個DIBSection物件的裝置場景 uStartIndex 顏色表中欲取回的第一個條目的索引 cEntries 欲取回的條目數量 pColors 這個結構陣列用於裝載顏色表資訊的第一個條目UINT SetDIBColorTable( HDC hDC, // handle to device context whose DIB is of interest UINT uStartIndex, // color table index of first entry to set UINT cEntries, // number of color table entries to set CONST RGBQUAD *pColors // pointer to array of color table entries );
VB宣告: Declare Function SetDIBColorTable Lib "gdi32" (ByVal hDC As Long, ByVal uStartIndex As Long, ByVal cEntries As Long, pColors As RGBQUAD) As Long 說明: 從選入裝置場景的DIBSection中取得顏色表資訊 返回值: 取回的顏色條目數量,零表示失敗。會設定GetLastError 引數: hDC 已選入了一個DIBSection物件的裝置場景 uStartIndex 顏色表中欲取回的第一個條目的索引 cEntries 欲取回的條目數量 pColors 這個結構陣列用於裝載顏色表資訊的第一個條目用DIB寫影像處理程式的時候,首先要明確一點:DIB並不是影像處理演算法,而是一種繪圖方法。處理演算法比DIB高一級,管理座標和顏色的運算;而DIB只是為了繪製。所以此時處理演算法的是速度的關鍵。 利用DIB繪製影像並沒有比用PSet/SetPixel繪製差多少,它只是把座標運算改成地址運算而已。很多人知道指標是一個危險的東西,就是因為它能直接訪問記憶體,如果指標不小心指錯地方的話,Windows立即報告一般保護性錯誤。所以,在地址運算的時候一定要小心,同時要注意隨時儲存,因為此時的操作的發生率非常高,否則辛辛苦苦寫的程式碼一瞬間沒了可別怪我沒提醒啊。
好了,現在開始! 由於處理演算法起指導作用,所以現在先講解VB_PSet。所有的程式碼都在FrmMain.frm中。其他的過程的程式碼可以不看,現在將注意力集中在“DrawIt”中,它就是管繪製的。 其實我這個演示程式蠻簡單的:R分量延著水平方向增加,G分量延著垂直方向增加,B分量則從右往左滾動。什麼?!“And &HFF”是什麼意思?!這可是基礎啊……(下略&HFFFF...字)。“&H”表示十六進位制數,而And表示按位與。&HFF是二進位制的“1111 1111”,正好覆蓋了低8位,這時用And進行按位與,只會得到低8位,與RGB分量需要的8位正好符合(對於“(J + K) And &HFF”來說,可以實現滾動效果)。 If Not 看明白了 Then Goto 前兩段 好!現在開啟VB_DIB。(由於24位能直接指定RGB分量,所以這裡是用的是24位DIB) 看了前面的“DIB的結構”,是不是有點昏呢?其實DIB也沒什麼,就是 一個表述點陣圖資訊的BITMAPINFO結構 和 一個儲存點陣圖資料的資料緩衝區,頂多再用SetDIBitsToDevice繪製,所以VB_DIB與VB_PSet相比只是多了SetDIBitsToDevice、BITMAPINFOHEADER(24位DIB沒有調色盤,所以用BITMAPINFOHEADER就行)和一些常數的宣告而已。由於這個演示程式不需要改變影像大小和色深,所以可以把有關變數作為窗體級變數,再在Form_Load中初始化。由於DIB並沒有向系統申請資源(陣列的記憶體是VB分配的,會自動釋放),所以不需要寫釋放程式碼。 現在來看DrawIt。
1.雖然可以逐點把座標對映成地址再寫,但是這樣效率太低了,可以利用座標處理的連續性進行。
2.由於我這裡用的是陣列,所以這裡用(陣列元素)索引代替地址。
3.最開始要注意DIB是逆序儲存的,要將索引設為最後一行第一個畫素的索引。
4.由於DIB的RGB順序是B、G、R,所以“MapData(CurIdx + ?)”的順序是2、1、0。
5.設定好一個畫素的顏色後,要注意把索引改為下個畫素的索引。
6.由於DIB是逆序儲存的,移到下一個掃描行是“LineIdx = LineIdx - LineBytes”
“好了,程式碼看懂了,按F5執行看看效果。”
“咦?速度好像沒快多少啊?”
這是由於程式在VB環境下是以解釋方式執行的,而解釋方式對做影像處理所需要大規模迴圈和大量的算術運算的執行效率很低,所以要編譯成(本機程式碼)exe再執行。此時還要注意編譯最佳化,可以把“高階最佳化”的所有勾打上,速度可提升20%左右。
雖然有GetDIBits/SetDIBits函式,但是DIB與GDI點陣圖之間的資料還是很不方便,特別處理過程中需要呼叫GDI函式來處理的時候。而且就算你不怕麻煩,但這樣做的處理效率很低。所以Windows為我們提供了DIBSection。DIBSection是一種特殊的DIB,它除了可以像DIB一樣直接對點陣圖資料所佔記憶體進行操作,它還可以選入DC、能用GDI函式繪製,非常靈活。但在VB下使用DIBSection還是有困難的,因為用CreateDIBSection建立DIBSection時,得到的是點陣圖資料的地址,而VB沒有指標。 所幸在VB下可以利用SafeArray結構的陣列模擬指標。關於模擬指標的原理、方法,網上的資料多的是,比如AdamBear的文章“ASP?id=12902">VB真是想不到系列之四:VB指標葵花寶典之SafeArray”。但這些文章都只是講一般性的應用,不能像真正的指標一樣隨意改變地址(他們都是使用CopyMemory改的)。而在影像處理中,由於點運算的頻繁,“像真正的指標一樣隨意改變地址”的功能很重要。 其實“像真正的指標一樣隨意改變地址”並沒有技術難度(對於已經學會模擬指標的人來說),就看想得到不:再用一個陣列(設pDataPtr)控制模擬指標(設pData)的地址,這樣可以用pDataPtr控制地址,用pData訪問資料。 由於需要建立兩個模擬指標,導致程式碼寫起來比較煩,於是我想寫個“模擬指標程式碼生成器”(在(ImgTest.zip的)MakeSAPtr目錄中)。雖然模擬指標建立方法有很多種,但是大多需要臨時變數來儲存地址,不適合批次生成,所以我只有改成這種方法(指“模擬指標程式碼生成器”自動生成的那種)。這種方法也許對 透過看AdamBear的文章學模擬指標的人 會造成困惑,這是因為AdamBear在說明VtrArray返回的值是SafeArray的地址的地址時沒講全:VarPtrArray返回的值 是 VB儲存陣列資訊的變數的地址,可透過改變該變數的值使陣列用另一個SafeArray。 現在來看VB_DIB_Ptr:
1.為了演示DIBSection能夠像HBITMAP一樣,我在Form_Load中建立了DC、將DIBSection選入DC。同時為了釋放,Form_UnLoad中寫了釋放程式碼。
2.在DrawIt中,注意處理部分的程式碼並沒有與VB_DIB差多少,只不過把索引計算改為地址運算而已。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-960767/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- (vb)利用GetBitmapBits,SetBitmapBits加速影像處理 (轉)
- 用VB編寫抽獎程式 (轉)
- 用VB編寫標準CGI程式 (轉)
- VB==容錯處理策略(全) (轉)
- VB與VC混合程式設計中處理訊息的方法 (轉)程式設計
- 傅立葉在影像處理應用
- 醫學影像處理中的資料讀寫
- 影像處理--影像特效特效
- 如何將GIMP(GNU 影像處理程式)轉換成PhotoShop?
- Python 影像處理 OpenCV (6):影像的閾值處理PythonOpenCV
- 用VB編寫簡單的程式來清空文件選單 (轉)
- webgl 影像處理2---影像畫素處理Web
- 編寫高效的MySQL應用(轉)MySql
- 影像的灰度變換——影像旋轉、影像的反色處理、對比度拉伸
- 數字影像處理學習筆記(1)——傅立葉變換在影像處理中的應用筆記
- 用VB學做“駭客”程式 (轉)
- 用VB編寫非同步多執行緒下載程式 (轉)非同步執行緒
- Python影像處理丨5種影像處理特效Python特效
- 影像處理---深入ManagedDirectX9(5)(轉)
- 影像處理---深入ManagedDirectX9(3)(轉)
- 影像處理---深入ManagedDirectX9(1)(轉)
- 影像預處理
- 批次影像處理 PhotoFoundry最新啟用版
- 影像處理的實現與應用(Ruby 版)
- 影像處理的實現與應用(Swift 版)Swift
- 影像處理的實現與應用(TypeScript 版)TypeScript
- 影像處理的實現與應用(PHP 版)PHP
- 影像處理的實現與應用(Elixir 版)
- Python 影像處理 OpenCV (7):影像平滑(濾波)處理PythonOpenCV
- 轉向C#,從細微處開始 -- 寫給VB、VC程式設計師 (轉)C#C程式程式設計師
- Henry的VB.NET之旅(十三)—標準事件處理程式事件
- 利用VB編寫螢幕保護程式 (轉)
- 用VB“破解”有時間限制的程式 (轉)
- (轉載)編寫高效的jQuery程式碼jQuery
- 在VB中用Paintpicture方法進行圖形處理 (轉)AI
- openCV中的影像處理 3 影像閾值OpenCV
- .NET 8 高效能跨平臺影像處理庫 ImageSharp
- FPGA影片、影像處理應用方向培訓FPGA