這篇文章算是個備忘吧...把自己截至目前為止的學習資源都放在這邊供以後查用:
System.Net.Sockets.Socket Class,這是實做Berkeley的通訊實體,算是最基礎的類別。
System.Net.Sockets.TcpClient,提供TCP用戶端連線的類別。
System.Net.Sockets.TcpListener,提供TCP伺服器端Listen Port(或是說Request)的類別。
System.Net.Socket.UDPClient,提供UDP客戶端連線的類別。
SocketAsyncEventArgs類別...這提供了一個解決I/O Bound process的一個類別,能夠更有效的把資源做利用。
CodeProject上有人對此類別做了一個簡單的Demo,在這裡能夠看得到。
不過這東西在對岸似乎很多人都感興趣,有不少文章都是問如何使用這個類別來完成網路通訊的服務,像是這篇他有放入他的壓力測試結果。
以後找到會繼續補上來。
2011年6月16日 星期四
2011年6月10日 星期五
Image Processing – Binarization
緒論:
影像處理是現在多媒體系統或影像監控系統是必備的功能與需求,而在進行諸多處理像是影像辨識等需求時,要對影像進行前置處理其中一樣就是二值化(二值化之前要對影像進行灰階的前置處理),把影像分成黑或白借此把影像的邊界與形狀取出,剔除相對不重要的資訊。而對於影像處理初出茅廬的我來說這就是一個最好的研究課題。這個領域的知識已經有很深的發展,其中更有些演算法的歷史比我還老,也就是這篇的重點:Otsu演算法。(ps.這篇編排先把實驗結果拉到前面實屬特例,這是為了要讓讀著馬上比較兩種演算法的不同,但話說前頭這兩種方法沒有誰一定比較好)
實驗數據與分析:
原圖來源:Google
Modified Sauvola:

Otsu:

原圖:

相關研究:
參考文獻:吳乾彌, 許志豪, "影像二值化演算處理器之軟硬整合設計與實現", 台科大電子工程, 98年六月
這是我在處理影像二值化中最後決定要引用的方法的參考文獻,之前是用鼎鼎大名的Otsu來當作影像閥值的演算法,當初試過一些照片覺得還不錯,但缺點就是閥值的計算是以整張圖片來統計,因此若有區域或局部性的亮度與整體差距過大就會造成該區域整塊的細節消失,算是美中不足的地方。
Otsu: 統計整張影像的灰階值,並以此計算值算出以哪個灰階值做劃分可以讓兩群的灰階值的變異數相加為最小,就口語來說就是找出用哪個數值化劃分可以讓兩個群體的群內差距為最小,亦即兩群的差異最大。
為了解決Otsu的痛處就要使用區域可適應性的閥值選擇方法,讓每個區域選擇自己的閥值,因此就找上了Sauvola,這篇,或這篇都有談到這個方法,當然這個方法我有稍稍改了一下,讓他對一個Window內的像素值賦予一個閥值,而非每個像素都有一個閥值,這樣可以減少不少計算量,換回不少計算時間。(ps.但是區域可適應性閥值選擇方法因為不像全域性計算一次閥值就好,因此會有比較多的計算時間)
Ostu的演算法概略如下:
(ps.我不大會用 blogger 編輯數學式子,就將就點看吧...囧rz)
若灰階影像其像速分布為 [1,2,3...x] (註:1~x 表示灰階影像像素的數值,範圍從 0~255),
可以計算出不同灰階值其分布機率值Pi:
Pi = ni / N --- (1)
其中 ni 代表灰階值為 i 的個數,N = n1+n2+n3+...+nx 為所有灰階值個數的總和。而所有Pi的總和為1。
summation Pi = 1, i from 1 to x ---(2)
上述有提到二值化是為了找出閥值讓兩個群體之間的差異最大,群內的差異最小。這裡有兩個群體,G1與G2,G1的灰階值分布為[1,2,3...y],G2的灰階值分布為[y+1,y+2,y+3,...x],由此計算出個群集的機率分布W1,W2。
W1 = summation Pi, i from 1 to y ---(3)
W2 = summation Pi, i from m+1 to x ---(4)
接著計算個群集的平均值 M1,M2。
M1 = summation i * Pi, i from 1 to y ---(5)
M2 = summation i * Pi, i from y+1 to x ---(6)
然後就可以利用上述的式子求出個群集的變異數 K1,K2。
K1 = summation (i - M1) * (i - M1) * Pi, i from 1 to y
K2 = summation (i - M1) * (i - M1) * Pi, i from y+1 to x
要找到一個閥值可以讓兩個群集的變異數相加最小。
研究方法:
(ps.因為原始程式碼有點長,這邊僅列出關鍵的部份。)
結論:
使用 Otsu 的好處莫過於計算速度快,理解上面有比較容易,是實作二值化入門方法的不二人選!其實在大多數的情況之下利用Otsu就已經可以把很多灰階影像很好的二值化了,只是在一些極端條件下(ex.不均勻的光源)會導致二值化的效果不彰,也因此才會有後續很多不同的二值化方法衍生出來。
參考文獻:
1.Otsu
http://en.wikipedia.org/wiki/Otsu's_method
2.Sauvola (原始論文)
http://www.sciencedirect.com/science/article/pii/S0031320399000552
3.Sauvola
http://gamera.sourceforge.net/doc/html/binarization.html#sauvola-threshold
特別附註:2012-08-27補
這篇文章其實一開始po上來的時候沒有想要寫的太完整,結果現在想補完整一點就變成複習一次了,把之前看的資料又重新看了一遍實在有點累。
影像處理是現在多媒體系統或影像監控系統是必備的功能與需求,而在進行諸多處理像是影像辨識等需求時,要對影像進行前置處理其中一樣就是二值化(二值化之前要對影像進行灰階的前置處理),把影像分成黑或白借此把影像的邊界與形狀取出,剔除相對不重要的資訊。而對於影像處理初出茅廬的我來說這就是一個最好的研究課題。這個領域的知識已經有很深的發展,其中更有些演算法的歷史比我還老,也就是這篇的重點:Otsu演算法。(ps.這篇編排先把實驗結果拉到前面實屬特例,這是為了要讓讀著馬上比較兩種演算法的不同,但話說前頭這兩種方法沒有誰一定比較好)
實驗數據與分析:
原圖來源:Google
Modified Sauvola:

Otsu:

原圖:

相關研究:
參考文獻:吳乾彌, 許志豪, "影像二值化演算處理器之軟硬整合設計與實現", 台科大電子工程, 98年六月
這是我在處理影像二值化中最後決定要引用的方法的參考文獻,之前是用鼎鼎大名的Otsu來當作影像閥值的演算法,當初試過一些照片覺得還不錯,但缺點就是閥值的計算是以整張圖片來統計,因此若有區域或局部性的亮度與整體差距過大就會造成該區域整塊的細節消失,算是美中不足的地方。
Otsu: 統計整張影像的灰階值,並以此計算值算出以哪個灰階值做劃分可以讓兩群的灰階值的變異數相加為最小,就口語來說就是找出用哪個數值化劃分可以讓兩個群體的群內差距為最小,亦即兩群的差異最大。
為了解決Otsu的痛處就要使用區域可適應性的閥值選擇方法,讓每個區域選擇自己的閥值,因此就找上了Sauvola,這篇,或這篇都有談到這個方法,當然這個方法我有稍稍改了一下,讓他對一個Window內的像素值賦予一個閥值,而非每個像素都有一個閥值,這樣可以減少不少計算量,換回不少計算時間。(ps.但是區域可適應性閥值選擇方法因為不像全域性計算一次閥值就好,因此會有比較多的計算時間)
Ostu的演算法概略如下:
(ps.我不大會用 blogger 編輯數學式子,就將就點看吧...囧rz)
若灰階影像其像速分布為 [1,2,3...x] (註:1~x 表示灰階影像像素的數值,範圍從 0~255),
可以計算出不同灰階值其分布機率值Pi:
Pi = ni / N --- (1)
其中 ni 代表灰階值為 i 的個數,N = n1+n2+n3+...+nx 為所有灰階值個數的總和。而所有Pi的總和為1。
summation Pi = 1, i from 1 to x ---(2)
上述有提到二值化是為了找出閥值讓兩個群體之間的差異最大,群內的差異最小。這裡有兩個群體,G1與G2,G1的灰階值分布為[1,2,3...y],G2的灰階值分布為[y+1,y+2,y+3,...x],由此計算出個群集的機率分布W1,W2。
W1 = summation Pi, i from 1 to y ---(3)
W2 = summation Pi, i from m+1 to x ---(4)
接著計算個群集的平均值 M1,M2。
M1 = summation i * Pi, i from 1 to y ---(5)
M2 = summation i * Pi, i from y+1 to x ---(6)
然後就可以利用上述的式子求出個群集的變異數 K1,K2。
K1 = summation (i - M1) * (i - M1) * Pi, i from 1 to y
K2 = summation (i - M1) * (i - M1) * Pi, i from y+1 to x
要找到一個閥值可以讓兩個群集的變異數相加最小。
研究方法:
(ps.因為原始程式碼有點長,這邊僅列出關鍵的部份。)
Int32[] GrayNum = new Int32[256];// 用來記錄灰階值的矩陣,這邊假設已經都匯入完畢 #region 計算各灰階值出現的機率。 IEnumerator enumerator = GrayNum.GetEnumerator(); List<Double> Property = new List<Double>(256); while (enumerator.MoveNext()) { Property.Add((Int32)enumerator.Current / TotalPixel); } #endregion #region 計算累積分佈函數。 Double[] w0 = new Double[256]; w0[0] = Property[0]; for (Int32 num = 1; num < Property.Count; ++num) { w0[num] = (w0[num - 1] + Property[num]); } #endregion
#region 區域變數的宣告。 Int32 OptimalOtsu = Int32.MinValue;// Otsu value Double entropyvalue = Double.MaxValue;// 熵值最佳值(越小越好)。 Double currententropy = Double.MinValue; Double category1 = Double.MinValue, category2 = Double.MinValue;// 存放一、二群的機率 Double mean1, mean2;// 第一、二群的加權平均數(期望值)。 Double Variance1, Variance2;// 第一、二群的變異數。 #endregion #region 開始計算閥值 // 閥值設在端點作用不大...(所以範圍設在1~254之間) for (Int32 threshold = 1; threshold <= 254; ++threshold) { #region 以各灰階值為閥值來計算兩群體的變異數總和。 // 避免分母為零。 category1 = w0[threshold] < 1e-9 ? 1e-9 : w0[threshold]; category2 = (1.0 - category1) < 1e-9 ? 1e-9 : (1.0 - category1); // 初始化區域變數。 mean1 = mean2 = 0.0; Variance1 = Variance2 = 0.0; // 計算兩群的加權平均數(期望值) for (Int32 val = 0; val <= threshold; val++) mean1 += val * Property[val] / category1; for (Int32 val = threshold + 1; val <= 255; val++) mean2 += val * Property[val] / category2; // 計算兩群的變異數。 for (Int32 val = 0; val <= threshold; val++) Variance1 += Math.Pow((val - mean1), 2) * Property[val]; Variance1 /= category1; for (Int32 val = threshold + 1; val <= 255; val++) Variance2 += Math.Pow((val - mean2), 2) * Property[val]; Variance2 /= category2; currententropy = Variance1 * category1 + Variance2 * category2; if (currententropy < entropyvalue) { entropyvalue = currententropy; OptimalOtsu = threshold; } } #endregion
結論:
使用 Otsu 的好處莫過於計算速度快,理解上面有比較容易,是實作二值化入門方法的不二人選!其實在大多數的情況之下利用Otsu就已經可以把很多灰階影像很好的二值化了,只是在一些極端條件下(ex.不均勻的光源)會導致二值化的效果不彰,也因此才會有後續很多不同的二值化方法衍生出來。
參考文獻:
1.Otsu
http://en.wikipedia.org/wiki/Otsu's_method
2.Sauvola (原始論文)
http://www.sciencedirect.com/science/article/pii/S0031320399000552
3.Sauvola
http://gamera.sourceforge.net/doc/html/binarization.html#sauvola-threshold
特別附註:2012-08-27補
這篇文章其實一開始po上來的時候沒有想要寫的太完整,結果現在想補完整一點就變成複習一次了,把之前看的資料又重新看了一遍實在有點累。
2011年4月20日 星期三
Studio Style

覺得自己的Visual Studio風格太死板一般了嗎?
趕快到 Studio Style 來選取你所喜愛的風格並換上去,換Color scheme就像換衣服一樣容易!
目前我所安裝的有兩個Scheme: son-of-obsidian、fluent
第一個scheme偏向深色顏色比較暗沉一點,而第二個也是深色但是顏色會偏亮一點,整體感覺會比較舒適。
fluent:

son-of-obsidian:

替換的方法也很簡單,從
工具→選項→環境→匯入匯出設定→
勾選「使用團隊設定檔案」→瀏覽所下載的scheme檔→Finish
趕快到 Studio Style 來選取你所喜愛的風格並換上去,換Color scheme就像換衣服一樣容易!
目前我所安裝的有兩個Scheme: son-of-obsidian、fluent
第一個scheme偏向深色顏色比較暗沉一點,而第二個也是深色但是顏色會偏亮一點,整體感覺會比較舒適。
fluent:

son-of-obsidian:

替換的方法也很簡單,從
工具→選項→環境→匯入匯出設定→
勾選「使用團隊設定檔案」→瀏覽所下載的scheme檔→Finish
2011年4月18日 星期一
在Visual Studio中建置兩份相同的組件

會興起這樣的念頭是因為我最近的工作中因為負責的子功能多達十項,每一次重新建置組件檔更新到專案中都要複製十次,實在十分繁瑣也因此起了個:「若能把所有專案建置的組件檔都集中在一處然後全部一起複製,接著貼上一個指定的資料夾中這樣就省事多了!」
上網找MSDN的說明找到這個:http://msdn.microsoft.com/zh-tw/library/42x5kfw4.aspx
這個MSDN上面的連結主要是說明建置命令的聚集的意含是什麼....對於客製化自己的建置指令非常有用處。
依照MSDN的說明那我的需求要如何達成呢?很容易!在建置後事件命令對話方塊中輸入:
copy "$(TargetDir)*.*" "$(SolutionDir)Libary.DLLPool\"
這段指令的意思就是 複製 TargetDir 中的所有檔案 放置到"方案的目錄中的Libary.DLLPool\"中(注意!這個路徑的最後面會加上一個反斜線)
這樣的話我的組件檔就會在Libary.DLLPool中也有相同的一份了!
Step1.於專案中按下右鍵選擇屬性
Step2.按下在建置後事件頁籤中的"建置後事件命令列"的建置後進行編輯按鈕

Step3.按下聚集按鈕來針對自己的需求進行插入與編輯(ex. copy "$(TargetDir)*.dll" "$(SolutionDir)Libary.DLLPool\" )

Step4.編輯完成按下重新建置,建置完成就會多一個組件副本在指定的資料夾中

===========================2012-02-15 新增================================
範例:
/* 判斷目前建置的平台為Debug或是Release模式來區分組件複製的位置。這邊要特別注意 batch 的寫法;要把判斷都寫完然後在用 goto 指令跳到實際執行的指令位置,執行完成後要用另外一個 goto 跳出函式之外。
*/
if $(ConfigurationName)==DeBug goto debug
if $(ConfigurationName)==Release goto release
:debug
copy $(TargetPath) $(SolutionDir)DLLPool\
goto finish
:release
copy $(TargetPath) $(SolutionDir)DLLPool_Release\
goto finish
:finish
上網找MSDN的說明找到這個:http://msdn.microsoft.com/zh-tw/library/42x5kfw4.aspx
這個MSDN上面的連結主要是說明建置命令的聚集的意含是什麼....對於客製化自己的建置指令非常有用處。
依照MSDN的說明那我的需求要如何達成呢?很容易!在建置後事件命令對話方塊中輸入:
copy "$(TargetDir)*.*" "$(SolutionDir)Libary.DLLPool\"
這段指令的意思就是 複製 TargetDir 中的所有檔案 放置到"方案的目錄中的Libary.DLLPool\"中(注意!這個路徑的最後面會加上一個反斜線)
這樣的話我的組件檔就會在Libary.DLLPool中也有相同的一份了!
Step1.於專案中按下右鍵選擇屬性


Step3.按下聚集按鈕來針對自己的需求進行插入與編輯(ex. copy "$(TargetDir)*.dll" "$(SolutionDir)Libary.DLLPool\" )

Step4.編輯完成按下重新建置,建置完成就會多一個組件副本在指定的資料夾中

===========================2012-02-15 新增================================
範例:
/* 判斷目前建置的平台為Debug或是Release模式來區分組件複製的位置。這邊要特別注意 batch 的寫法;要把判斷都寫完然後在用 goto 指令跳到實際執行的指令位置,執行完成後要用另外一個 goto 跳出函式之外。
*/
if $(ConfigurationName)==DeBug goto debug
if $(ConfigurationName)==Release goto release
:debug
copy $(TargetPath) $(SolutionDir)DLLPool\
goto finish
:release
copy $(TargetPath) $(SolutionDir)DLLPool_Release\
goto finish
:finish
2011年2月21日 星期一
在.NET3.5的環境中使用TPL!

微軟目前最新釋出的.NET4.0環境中已經把大勢所趨的平行計算給納入標準的Lib中了,所以我們可以直接在.NET4.0的環境中直接使Parallel類別中的方法,體驗到平行計算帶來的好處,不過.NET3.5的環境就沒有直接內入Lib中...好在微軟有提供額外的Extension供使用者下載...http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx
下載之後是一個msi檔,click install之後預設的路徑為:C:\Program Files (x86)\Microsoft Cloud Programmability\Reactive Extensions\v1.0.2838.0\Net35
底下有個System.Threading.dll把這個東西放入方案中的參考並在cs檔中using System.Threading;這個namespace就可以如同在.NET 4.0中使用TPL一樣,免去自己分配工作給Thread的麻煩。
下載之後是一個msi檔,click install之後預設的路徑為:C:\Program Files (x86)\Microsoft Cloud Programmability\Reactive Extensions\v1.0.2838.0\Net35
底下有個System.Threading.dll把這個東西放入方案中的參考並在cs檔中using System.Threading;這個namespace就可以如同在.NET 4.0中使用TPL一樣,免去自己分配工作給Thread的麻煩。
訂閱:
文章 (Atom)