C#本身自帶有一定的圖像處理能力,即使在不依賴Emgu CV的情況下,也是有很大的潛質的。
不過,最近在處理大量圖片時,發現圖片數量較少時,處理本身所帶來的延時不會讓人敏感,但是數量較大時,程序花費大量時間在預處理圖片上,導致程序很容易誤報線程時延過大,導致誤判程序異常。對於這個問題苦惱很久,畢竟不是CS專業出身,對於圖像處理以及一些算法和庫的運用上感到畢竟吃力。
今天在閱讀了一些數字圖像處理的書之后感到收益很大,現在來做點試驗對照一下之前自己的錯誤在哪里。
之前看MS的C#API時候發現有一個Graphics挺適合我現在使用,於是便二話不說開干,今天才發現越來封裝的太好,反而會帶來許多拖累,不如操起指針暢快的運行。
不多說,試驗對比的是Graphics、操作內存、操作指針三者在性能上差距。
試驗內容:對同一張照片進行灰度處理,直接看三者之間延時有多大
1.試驗基本場景,比較丑陋,就湊合看着。。。

2.提取像素Button中,調用Graphics灰度化處理
private void button2_Click(object sender, EventArgs e)
{
if (curBitMap1 != null)
{
Color curColor;
int ret;
//個人運行時間判斷類
GetRunTime getRunTime = new GetRunTime();
//stopWatch啟動
getRunTime.Start();
//Graphics//灰度化
for (int i = (int)(0); i < (int)(curBitMap1.Width ); i++)
{
for (int j = 0; j < curBitMap1.Height; j++)
{
//獲取像素
curColor = curBitMap1.GetPixel(i, j);
//獲取RGB
ret = (int)(curColor.R * 0.299 + curColor.G * 0.587 + curColor.B * 0.114);
//設置像素
curBitMap1.SetPixel(i, j, Color.FromArgb(ret, ret, ret));
}
}
Invalidate();
labelGetPixel.Text = "提取像素花費時間:" + getRunTime.getRunTime() + "ms";
}
}
3.內存法Button中,直接復制數據到內存,直接操作
private void button5_Click(object sender, EventArgs e)
{
if (curBitMap2 != null)
{
GetRunTime getRunTime = new GetRunTime();
getRunTime.Start();
Rectangle rect = new Rectangle(0, 0, curBitMap2.Width, curBitMap2.Height);
//以讀寫方式鎖定位圖
System.Drawing.Imaging.BitmapData bmpData =
curBitMap2.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, curBitMap2.PixelFormat);
//首地址
IntPtr ptr = bmpData.Scan0;
//24位bmp的總字節數
int bytes = curBitMap2.Width * curBitMap2.Height * 3;
byte[] rgbValues = new byte[bytes];
//復制被鎖定的圖像到rgbValues
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
double colorTemp = 0;
//灰度化
for (int i = 0; i < bmpData.Height; i++)
{
for (int j = 0; j < bmpData.Width * 3; j+=3)
{
//跳過空白塊
colorTemp = rgbValues[i * bmpData.Stride + j + 2] * 0.299
+ rgbValues[i * bmpData.Stride + j + 1] * 0.587
+ rgbValues[i * bmpData.Stride + j] * 0.114;
rgbValues[i * bmpData.Stride + j + 2]
= rgbValues[i * bmpData.Stride + j + 1]
= rgbValues[i * bmpData.Stride + j] = (byte)colorTemp;
}
}
//復制回位圖
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
curBitMap2.UnlockBits(bmpData);
Invalidate();
labelCaChe.Text = "提取像素內存法花費時間:" + getRunTime.getRunTime() + "ms";
}
}
4.指針法Button中,直接復制數據到內存,指針操作
private void button6_Click(object sender, EventArgs e)
{
GetRunTime getRunTime = new GetRunTime();
getRunTime.Start();
Rectangle rect = new Rectangle(0, 0, curBitMap3.Width, curBitMap3.Height);
//以讀寫方式鎖定位圖
System.Drawing.Imaging.BitmapData bmpData =
curBitMap3.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, curBitMap3.PixelFormat);
byte temp = 0;
unsafe
{
//首地址
byte* ptr = (byte*)(bmpData.Scan0);
//灰度化
for (int i = 0; i < bmpData.Height; i++)
{
for (int j = 0; j < bmpData.Width;j++ )
{
temp = (byte)(0.299 * ptr[2] + 0.587 * ptr[1] + 0.114 * ptr[0]);
ptr[0] = ptr[1] = ptr[2] = temp;
ptr += 3;
}
//跳過空白塊
ptr += bmpData.Stride - bmpData.Width * 3;
}
}
//解鎖位圖
curBitMap3.UnlockBits(bmpData);
Invalidate();
labelPoint.Text = "提取像素內存法花費時間:" + getRunTime.getRunTime() + "ms";
}
5,簡單完善一下就開始打開圖片看看運行時間之間的對比吧


總結:雖然電腦爛,但還是看得出三者之間的差距吧。復制進內存直接操作和用指針操作都是極大的提高效率,如果處理一張圖片節約60ms,那處理大量的圖片時候將會節約多少時間?所以今后處理圖像的時候,如果不用Emgu,盡量也別用Graphics,還是自己動手操作內存。(肯定會有人說,你為毛不去用C++,好吧,我其實就是在黑Graphics,你咬我呀~)
