C#圖片處理常見方法效能比較

來源:互聯網
上載者:User

在.NET編程中,由於GDI+的出現,使得對於映像的處理功能大大增強。在文通過一個簡單黑白處理執行個體介紹在.NET中常見的圖片處理方法和原理並比較各種方法的效能。

 

黑白處理原理:彩色影像處理成黑白效果通常有3種演算法;

(1).最大值法: 使每個像素點的 R, G, B 值等於原像素點的 RGB (顏色值) 中最大的一個;

(2).平均值法: 使用每個像素點的 R,G,B值等於原像素點的RGB值的平均值;

(3).加權平均值法: 對每個像素點的 R, G, B值進行加權

自認為第三種方法做出來的黑白像最 "真實".

 

1.GetPixel方法

 GetPixel(i,j)和SetPixel(i, j,Color)可以直接得到映像的一個像素的Color結構,但是處理速度比較慢.

 /// <summary>
/// 像素法
/// </summary>
/// <param name="curBitmap"></param>
private void PixelFun(Bitmap curBitmap)
{
int width = curBitmap.Width;
int height = curBitmap.Height;
for (int i = 0; i <width; i++) //這裡如果用i<curBitmap.Width做迴圈對效能有影響
{
for (int j = 0; j < height; j++)
{
Color curColor = curBitmap.GetPixel(i, j);
int ret = (int)(curColor.R * 0.299 + curColor.G * 0.587 + curColor.B * 0.114);
curBitmap.SetPixel(i, j, Color.FromArgb(ret, ret, ret));
}
}
}

這裡提一下,在迴圈次數控制時盡量不要用i<curBitmap.Width做迴圈條件,而是應當將其取出儲存到一個變數中,這樣迴圈時不用每次從curBitmp中取Width屬性,從而提高效能。

儘管如此,直接提取像素法對大像素圖片處理力不從心,處理一張1440*900的圖片耗時2182ms.本人配置單:

處理之前:

處理後:

 

可以直觀地看出用時間2056ms.多次測試有少許波動。

2.記憶體拷貝法
記憶體拷貝法就是採用System.Runtime.InteropServices.Marshal.Copy將映像資料拷貝到數組中,然後進行處理,這不需要直接對指標進行操作,不需採用unsafe,處理速度和指標處理相差不大,處理一副1440*900的映像大約需要34ms。


記憶體拷貝發和指標法都需用到的一個類:BitmapData

BitmapData類


BitmapData對象指定了位元影像的屬性


1.       Height屬性:被鎖定位元影像的高度.


2.       Width屬性:被鎖定位元影像的高度.


3.       PixelFormat屬性:資料的實際像素格式.


4.       Scan0屬性:被鎖定數組的首位元組地址,如果整個映像被鎖定,則是映像的第一個位元組地址.


5.       Stride屬性:步幅,也稱為掃描寬度.

如所示,數組的長度並不一定等於映像像素數組的長度,還有一部分未用地區,這涉及到位元影像的資料結構,系統要保證每行的位元組數必須為4的倍數.

假設有一張圖片寬度為6,因為是Format24bppRgb格式(每像素3位元組。在以下的討論中,除非特別說明,否則Bitmap都被認為是24位RGB),顯然,每一行需要6*3=18個位元組儲存。對於Bitmap就是如此。但對於BitmapData,雖然BitmapData.Width還是等於Bitmap.Width,但大概是出於顯示效能的考慮,每行的實際的位元組數將變成大於等於它的那個離它最近的4的整倍數,此時的實際位元組數就是Stride。就此例而言,18不是4的整倍數,而比18大的離18最近的4的倍數是20,所以這個BitmapData.Stride = 20。顯然,當寬度本身就是4的倍數時,BitmapData.Stride = Bitmap.Width * 3。

畫個圖可能更好理解(此圖僅代表PixelFormat= PixelFormat. Format24bppRgb時適用,每個像素佔3個位元組共24位)。R、G、B 分別代表3個原色分量位元組,BGR就表示一個像素。為了看起來方便我在每個像素之間插了個空格,實際上是沒有的。X表示補足4的倍數而自動插入的位元組。為了符合人類的閱讀習慣我分行了,其實在電腦記憶體中應該看成連續的一大段。

Scan0

|-------Stride-----------|
|-------Width---------|  |
BGR BGR BGR BGR BGR BGR XX
BGR BGR BGR BGR BGR BGR XX
BGR BGR BGR BGR BGR BGR XX
.
則對於Format24bppRgb格式,滿足:

BitmapData.Width*3 + 每行未使用空間(的XX)=BitmapData.Stride

 

同理,很容易推倒對於Format32bppRgb或Format32bppPArgb格式,滿足:

BitmapData.Width*4 + 每行未使用空間(的XX)=BitmapData.Stride

  /// <summary>
/// 記憶體拷貝法
/// </summary>
/// <param name="curBitmap"></param>
private unsafe void MemoryCopy(Bitmap curBitmap)
{
int width = curBitmap.Width;
int height = curBitmap.Height;

Rectangle rect = new Rectangle(0, 0, curBitmap.Width, curBitmap.Height);
System.Drawing.Imaging.BitmapData bmpData = curBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,PixelFormat.Format24bppRgb);//curBitmap.PixelFormat

IntPtr ptr = bmpData.Scan0;
int bytesCount = bmpData.Stride * bmpData.Height;
byte[] arrDst = new byte[bytesCount];
Marshal.Copy(ptr, arrDst, 0, bytesCount);

for (int i = 0; i < bytesCount; i+=3)
{
byte colorTemp = (byte)(arrDst[i + 2] * 0.299 + arrDst[i + 1] * 0.587 + arrDst[i] * 0.114);
arrDst[i] = arrDst[i + 1] = arrDst[i + 2] = (byte)colorTemp;

}
Marshal.Copy(arrDst, 0, ptr, bytesCount);
curBitmap.UnlockBits(bmpData);
}

 3.指標法

指標在c#中屬於unsafe操作,需要用unsafe括起來進行處理,速度最快,處理一副180*180的映像大約需要18ms。

 

採用byte* ptr = (byte*)(bmpData.Scan0); 擷取映像資料根位置的指標,然後用bmpData.Scan0擷取映像的掃描寬度,就可以進行指標操作了。

 

    /// <summary>
/// 指標法
/// </summary>
/// <param name="curBitmap"></param>
private unsafe void PointerFun(Bitmap curBitmap)
{
int width = curBitmap.Width;
int height = curBitmap.Height;

Rectangle rect = new Rectangle(0, 0, curBitmap.Width, curBitmap.Height);
System.Drawing.Imaging.BitmapData bmpData = curBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,PixelFormat.Format24bppRgb );//curBitmap.PixelFormat
byte temp = 0;
int w = bmpData.Width;
int h = bmpData.Height;
byte* ptr = (byte*)(bmpData.Scan0);
for (int i = 0; i < h; i++)
{
for (int j = 0; j <w; j++)
{
temp = (byte)(0.299 * ptr[2] + 0.587 * ptr[1] + 0.114 * ptr[0]);
ptr[0] = ptr[1] = ptr[2] = temp;
ptr +=3; //Format24bppRgb格式每個像素佔3位元組
}
ptr += bmpData.Stride - bmpData.Width * 3 ;//每行讀取到最後“有用”資料時,跳過未使用空間XX
}
curBitmap.UnlockBits(bmpData);
}
以下是多組測試資料:
 
1920*1080
1440*900
1208*800
1024*768
500*544
200*169
直接提取像素法
1705ms
1051ms
1710ms
1340ms
450ms
32ms
記憶體拷貝法
54ms
33ms
26ms
20ms
7ms
0ms
指標法
28ms
17ms
14ms
10ms
3ms
0ms
 
由此可見,指標法與直接提取像素法效率竟隔兩個數量級!
 比較以上方法優缺點:1.總體上效能 指標法略強於記憶體拷貝法,直接提取像素法效能最低;2.對大圖片處理指標法和記憶體拷貝法效能提升明顯,對小圖片都比較快;3.直接提取像素法簡單易用,而且不必關注圖片像素格式(PixelFormat),為安全的程式碼;記憶體拷貝法和指標法如果不改變原圖片像素格式要針對不同的像素格式做不同的處理,且為不安全的程式碼。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.