在圖片處理過程中,我們經常需要對圖片逐像素進行處理,比如為了使圖片某一向量的顏色加深或者減淡,或者為了使圖像變化成黑白顏色,這個時候我們需要取出每個點上的像素進行計算,再賦值到圖像指定的位置。在.Net中,官方提供了Image.GetPixel(int x, int y)的方法供開發人員獲取指定位置的像素,同時提供了Image.SetPixel(int x, int y, Color color)的方法來給指定位置的像素賦值。但是這個方法性能很差,假設存在一張1024*768的圖片,逐像素操作並予以緩存的話亦至少需要1027*768次GetPixel和SetPixel,處理速度將慢到無法忍受。因此本方案將使用對內存直接讀取和賦值的方式來提高圖片處理的速度。
這裡首先要介紹一個類System.Drawing.Imaging.BitmapData,直接實例化這個類沒有用處,我們需要將一個Bitmap鎖定到內存中,來獲取一個BitmapData的實例。方法如下:
使用Bitmap.LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format)或者它的另一個重載Bitmap.LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format, BitmapData bitmapData)來將圖像數據鎖定到內存中,以此來獲取一個與指定圖片相關聯的BitmapData實例。
在BitmapData中有一個重要的屬性Scan0,它是一個指針,指向了圖片數據所在內存的第一個位置。使用內存跟蹤,將Scan0的值填入地址中,可以看到內存的分配情況(Format32bppArgb顏色深度):
這些值與圖片像素的對應關系是這樣的:
現在我們可以使用System.Runtime.InteropServices.Marshal.WriteByte(IntPtr ptr, byte val)的方法來更改指定位置的像素值了,修改後只要再調用一次Bitmap.UnlockBits(BitmapData bitmapdata)來解鎖內存就可以了,例如:
以下為引用的內容:此示例將圖片上所有像素的Red向量設置為255。運行此實例可以看到圖片變色了。
每次調用System.Runtime.InteropServices.Marshal.WriteByte(IntPtr ptr, byte val)的方法並不方便,因此我們構造一個ColorBgra類用來儲存這4個顏色向量,它的主要代碼是這樣的(參考自Paint.Net提供的源碼):
以下為引用的內容:使用這個類在聲明為unsafe的上下文中就可以通過計算偏移量的辦法尋址找到指定位置像素的地址(指針),例如在Format32bppArgb顏色深度的圖片中可以這樣計算:
以下為引用的內容:將計算返回的指針賦給ColorBgra*。之後使用如下方法:
以下為引用的內容:直接把值寫入內存中,實現對圖片像素的快速操作。