Image Porcessing Using TPL Contrast Enhancement

影像處理是很注重效率與正確性的,因此普遍都會建議使用平行計算,或是使用指標的方式來增加效能。在 .NET Framework 下使用 C# 這兩種方式都可以使用,只是若用 System.Threading.Task 類別下的方法對於未來的擴展以及程式碼的可閱讀性會有較佳的表現,畢竟M$提供這麼好的工具不用可惜,再說也不是用 C/C++ 來開發…(不過他們也有平行處理的類別庫可以用)。 底下的範例程式是影像強化中的對比強化,輸入的參數為欲處理的影像以及強化的程度。至於那個強化程度的計算與轉換可以參考其他網路上面的作法。 會用到的命名空間: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices;// 使用Marshal類別來配置Unmanaged記憶體。 using System.Threading.Tasks;// TPL //處理邏輯: public Bitmap Contraster(Bitmap bmp, Int32 level) { Double dlevel = Math.Sqrt((level % 10) + 1); Bitmap newbmp = new Bitmap(bmp.Width, bmp.Height); // orignal bitmap only for reading value, the clone one for writing new value. BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat); BitmapData newData = newbmp.LockBits(new Rectangle(0, 0, newbmp.Width, newbmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat); Int32 Width = bmp.Width; Int32 Height = bmp.Height; Parallel.For(0, Height, delegate(Int32 height) { Parallel.For(0, Width, delegate(Int32 width) { Int32 Offset = height * bmpData.Stride + width * (bmpData.Stride / bmpData.Width); Double Red = Marshal.ReadByte(bmpData.Scan0, Offset + 2); Double Green = Marshal.ReadByte(bmpData.Scan0, Offset + 1); Double Blue = Marshal.ReadByte(bmpData.Scan0, Offset); Red = ((Red / 255.0 - 0.5) * dlevel + 0.5) * 255; Red = Red > 255 ? 255 : Red; Red = Red < 0 ? 0 : Red; Green = ((Green / 255.0 - 0.5) * dlevel + 0.5) * 255; Green = Green > 255 ? 255 : Green; Green = Green < 0 ? 0 : Green; Blue = ((Blue / 255.0 - 0.5) * dlevel + 0.5) * 255; Blue = Blue > 255 ? 255 : Blue; Blue = Blue < 0 ? 0 : Blue; Marshal.WriteByte(newData.Scan0, Offset + 2, (Byte)Red); Marshal.WriteByte(newData.Scan0, Offset + 1, (Byte)Green); Marshal.WriteByte(newData.Scan0, Offset, (Byte)Blue); }); }); bmp.UnlockBits(bmpData); newbmp.UnlockBits(newData); return newbmp; }

July 21, 2011 · 2 分鐘 · 287 字

Image Processing - Binarization

影像處理是現在多媒體系統或影像監控系統是必備的功能與需求,而在進行諸多處理像是影像辨識等需求時,要對影像進行前置處理其中一樣就是二值化(二值化之前要對影像進行灰階的前置處理),把影像分成黑或白借此把影像的邊界與形狀取出,剔除相對不重要的資訊。而對於影像處理初出茅廬的我來說這就是一個最好的研究課題。這個領域的知識發展由來已久,而這篇的重點,Otsu演算法,更是這方面的研究論文幾乎必定會比較的方法。(ps.這篇編排先把實驗結果拉到前面實屬特例,這是為了要讓讀著馬上比較兩種演算法的不同,但話說在前頭這兩種方法沒有誰一定比較好) 實驗數據與分析: 原圖來源:Google Image 原圖: Modified Sauvola: Otsu: 相關研究: 參考文獻:吳乾彌, 許志豪, “影像二值化演算處理器之軟硬整合設計與實現”, 台科大電子工程, 98年六月 paper 這是我在處理影像二值化中最後決定要引用的方法的參考文獻,之前是用鼎鼎大名的Otsu來當作影像閥值的演算法,當初試過一些照片覺得還不錯,但缺點就是閥值的計算是以整張圖片來統計,因此若有區域或局部性的亮度與整體差距過大就會造成該區域整塊的細節消失,算是美中不足的地方。 Otsu 統計整張影像的灰階值,並以此計算值算出以哪個灰階值做劃分可以讓兩群的灰階值的變異數相加為最小,就口語來說就是找出用哪個數值化劃分可以讓兩個群體的群內差距為最小,亦即兩群的差異最大。 為了解決 Otsu 的痛處就要使用區域可適應性的閥值選擇方法,讓每個區域選擇自己的閥值,因此就找上了 Sauvola ,這篇,或這篇都有談到這個方法,當然這個方法我有稍稍改了一下,讓他對一個 Window 內的像素值賦予一個閥值,而非每個像素都有一個閥值,這樣可以減少不少計算量,換回不少計算時間。(ps.但是區域可適應性閥值選擇方法因為不像全域性計算一次閥值就好,因此會有比較多的計算時間) Ostu 的演算法概略如下: 若灰階影像其像速分布為 [1,2,3…x] (註:1x 表示灰階影像像素的數值,範圍從 0255), 可以計算出不同灰階值其分布機率值$P_i$: $$P_i = n_i / N —(1)$$ 其中 $n_i$ 代表灰階值為 i 的個數,$N = n_1+n_2+n_3+…+n_x$ 為所有灰階值個數的總和。而所有$P_i$的總和為1。 $$\sum_{i=1}^xP_i —(2)$$ 上述有提到二值化是為了找出閥值讓兩個群體之間的差異最大,群內的差異最小。這裡有兩個群體,G1與G2,G1的灰階值分布為[1,2,3…y],G2的灰階值分布為[y+1,y+2,y+3,…x],由此計算出個群集的機率分布W1,W2。 $$W1 = \sum_{i=1}^yP_i —(3)$$ $$W2 = \sum_{i=y+1}^xP_i —(4)$$ 接著計算個群集的平均值 M1,M2。 $$M1 = \sum_{i=1}^y i*P_i —(5)$$ ...

June 10, 2011 · 2 分鐘 · 417 字