最近由於項目上需要對圖片進行二值化處理,就學習了相關的圖片處理上的知識,從開始的二值化的意義到動態閥值檢測二值化等等,並用C#得以應用,學到了很多的知識和大家分享下我個人的經驗,希望對大家有幫助。
二值化
二值化簡而言之是對一副彩色圖片進行0/1運算,最終顯示一副黑白相間的圖片,其意義多數在於對二值化處理後的圖片進行分割識別,一些自動識別的驗證碼工具大多是先進行二值化,然後在模式識別,最終推斷出驗證碼;我的項目中是由於硬件只支持黑色和白色,所以要對用戶的圖片進行處理,然後顯示在硬件上。
在深入了解二值化的過程中就發現了很多有意思,或者說十分令我感興趣的東西,就是各種圖片處理算法。
因為一個普通,色彩少,圖像相對簡單的圖片經二值化處理後還算能勉強接受。但是一副精美的圖片在經過二值化處理後顯得十分難看,或根本看不出圖片有任何意義。這其中大家就開始研究思考。
先補充下知識,因為我覺得如果不把基本原理講清楚的話,可能大家不會發現這些算法是多麼的有意思(當然,也可能是我太喜歡這些東西而已)。過程是這樣的,一副彩色的圖片要先進行灰度化(有的是 將R+G+B加起來除以3取平均值再付給R=G=B,也可以依據權值進行灰度劃分,如(0.299 * r + 0.587 * g + 0.114 * b) 這是一個依據人眼對不同RGB顏色的區分度進行優化的灰度算法,很有意思,想不到人類對不同顏色識別輕重還不一樣)。在經過灰度化之後,實際每個顏色的色值是 R=G=B=(0-255之間)的數值,這樣我們當然可以依據127劃分,如果小於127則認為接近黑色0,反之則認為接近白色255,將所有色值依據127劃分後圖像就成為黑白的二值化圖片。
回過頭來,我們來看看,這樣經過二值化處理的圖片“失真”還是很嚴重,有沒有什麼辦法能優化呢,當然這難不倒這些研究算法的專家們。
Ordered dithering有序抖動就是一個化腐朽為神奇的算法,具體算法細節不去深究,大概就是依據一個算法矩陣,然後對圖像點進行處理。下面是圖片對比。
這幅圖片是原圖
這幅圖是已128為全局閥值的二值化圖片
這幅圖是有序抖動處理後的二值化圖片,黑白的二值化圖片(並非灰度),所有的點非黑即白,視覺上會產生灰度圖片的視覺誤差,這就是神奇之處。(注:原圖如果是大圖的話,效果更明顯。)
此外,還有很多優秀的算法對圖片進行處理,大都是圍繞如何處理判斷“閥值”而產生的。
AForge.Net.Image
在查找C#開源類庫的時候,發現了強大的AForge.Net,您可以先參考其官方網站了解更多詳情。這個開源類庫實在是太強大了,不僅包含圖片處理的各種算法方式,視頻處理方式,還包含人工智能方面的各種實踐,都是基於C#寫的,代碼整潔程度也是值得學習的,所以,今後如果有時間應該仔細研究研究。而且官方文檔及Sample都十分完善,十分強大。
N多種圖片處理方式,參照Demo,你會發現使用起來極其簡單~~
代碼樣例
啰嗦了那麼多,下面就演示下代碼如何實現的:
這裡代碼可能不全,請參照官方AForge.NET Framework-2.2.5\Samples\Imaging\FiltersDemo這個Demo
復制代碼
Bitmap temp = AForge.Imaging.Image.Clone(new Bitmap(SrcPic), PixelFormat.Format24bppRgb); // 加載圖片,並強制轉換成Format24bppRgb這種格式
temp = Grayscale.CommonAlgorithms.RMY.Apply(temp); // 將圖片依照RY算法進行灰度化,很多算法都是先灰度然後再處理的。
pictureBox.Image =(new OrderedDithering()).Apply(sourceImage); // 應用Filter,這裡選取OrderedDithering類型的Filter
復制代碼
這是應用AForge.Net實現的多種處理圖片的代碼,很簡單並且擴展性很強,值得學習。
更多代碼請參考官方Sample,有任何問題,請回復我。
後記
雖然有了十分強大的AForge.Net,但是針對一些特定圖片處理需求還是要自己寫代碼的,當然也可以用AForge實現,這裡我只是強調一下如果自己手動寫代碼的話是如何處理的並且有哪些需要注意的地方。
首先從彩色圖片灰度化說起:所謂灰度化就是按照一定的算法將R,G,B的值轉換成同一個值,這其中比較普遍的做法一個是(R+G+B)/3取平均值,另一個是加權算法依據人眼對不同顏色的識別而權值化的算法 (0.299 * r + 0.587 * g + 0.114 * b) = R=G=B。
復制代碼
/// <summary>
/// 灰度化實現
/// </summary>
/// <param name="bmp"></param>
/// <param name="foo"></param>
/// <returns></returns>
private static Bitmap WeightGrayScaleImple(Bitmap bmp, Func<double, double, double, byte> foo)
{
Bitmap thisMap = bmp;
Rectangle rect = new Rectangle(0, 0, thisMap.Width, thisMap.Height);
BitmapData bmpData = thisMap.LockBits(rect, ImageLockMode.ReadWrite, thisMap.PixelFormat);
unsafe
{
byte* ptr = (byte*)(bmpData.Scan0);
for (int i = 0; i < bmpData.Height; i++)
{
for (int j = 0; j < bmpData.Width; j++)
{
ptr[0] = ptr[1] = ptr[2] = foo(ptr[2], ptr[1], ptr[0]);
ptr += 4;
}
ptr += bmpData.Stride - bmpData.Width * 4;
}
}
thisMap.UnlockBits(bmpData);
return thisMap;
}
復制代碼
復制代碼
// Foo 實現
private static byte WeightGrayBinaraztion(double r, double g, double b)
{
return (byte)(0.299 * r + 0.587 * g + 0.114 * b);// Feature Weight
}
復制代碼
注:在C#下對圖片值這種指針類型的處理時,必須啟用unsafe,否則效率極其低。(項目中打開unsafe開關:項目屬性--->生成--->允許不安全代碼)
基本上以上內容就是項目中所用到的處理圖片的所有內容,希望通過以上內容的介紹對大家有幫助。 有問題,歡迎回復,謝謝。