1. 程式人生 > >Halcon閾值化運算元dual_threshold和var_threshold的理解

Halcon閾值化運算元dual_threshold和var_threshold的理解

Halcon中閾值二值化的運算元眾多,通常用得最多的有threshold、binary_threshold、dyn_threshold等。 threshold是最簡單的閾值分割運算元,理解最為簡單;binary_threshold是自動閾值運算元,它可以自動選出暗(dark)的區域,或者自動選出亮(light)的區域,理解起來也沒有難度。  動態閾值運算元dyn_threshold理解起來稍微複雜一點,使用dyn_threshold運算元的步驟基本是這樣的:  ① 將原圖進行濾波平滑處理。  ② 用原圖和平滑後的圖逐個畫素做比較,它可以根據引數分割出原圖比平滑後的圖灰度高(或者低)若干個灰度值的區域。  舉例如下: 

  處理程式是這樣的:  1 read_image (Image, 'C:/Users/happy xia/Desktop/dynPic.png') 2 mean_image (Image, ImageMean, 9, 9) 3 dyn_threshold (Image, ImageMean, RegionDynThresh, 10, 'dark')  程式分析:本例中,將圖片模糊後,點陣字的黑色擴散了,隨之就是字的黑色不如原圖那麼黑了,那麼通過給定的限值“10”和“dark”,就可以將原圖比模糊後的圖暗10個灰階以上的區域(即黑色文字部分)選出來了。  以上所說的三個運算元並不是本文的重點,但卻是理解下面的兩個閾值分割運算元的準備知識。 1、dual_threshold
  先看程式和效果圖再分析。   1 read_image (Image, 'C:/Users/happy xia/Desktop/2.png') 2 dual_threshold (Image, RegionCrossings, 174, 200, 180 dual_threshold(Image : RegionCrossings : MinSize, MinGray, Threshold : )  該運算元簽名中:Threshold 表示用於分割的閾值數值,MinSize表示分割出來的區域的最小面積(即數畫素的數目個數),MinGray表示分割出來的區域對應的原圖中影象畫素的最高灰度不能低於MinGray
設定值。  注意圖中藍色矩形小色塊的面積是175個畫素,因此當MinSize = 174時,它可以被分割出來。  OK,我知道這麼說比較拗口。下面我邊改變引數邊觀察效果圖,並做簡要分析:  1 read_image (Image, 'C:/Users/happy xia/Desktop/2.png') 2 dual_threshold (Image, RegionCrossings, 176, 200, 180)  效果圖如下:   由於最小面積設定為176,那麼面積為175畫素的矩形小色塊就沒有被分割出來。  再來改變MinGray引數:  1 read_image (Image, 'C:/Users/happy xia/Desktop/2.png') 2 dual_threshold (Image, RegionCrossings, 176, 216, 180)   此時觀察到,最右邊那個齒輪本來分割出來的區域沒有了!  通過取色器觀察可知,這塊區域最亮的灰度大概比211高一點點。   我們把這個值略微調低再看看:  1 read_image (Image, 'C:/Users/happy xia/Desktop/2.png') 2 dual_threshold (Image, RegionCrossings, 176, 210, 180)   最右邊那個齒輪右下角那一塊又被分割出來了!  相信通過這樣引數的反覆調節,大家已經徹底明白了dual_threshold運算元的意義和用法。  我們看這個運算元的名稱——dual是“雙”的意思,也就是雙閾值。如果我們讓引數列表中的MinGray = Threshold,那就是單閾值了。  1 read_image (Image, 'C:/Users/happy xia/Desktop/2.png') 2 dual_threshold (Image, RegionCrossings, 176, 180, 180)  這個運算元是很高效的。如果要完成上面這個程式這樣的功能,用threshold運算元的話,程式碼要這樣寫:  1 read_image (Image, 'C:/Users/happy xia/Desktop/2.png') 2 threshold (Image, Region, 180, 255) 3 connection (Region, ConnectedRegions) 4 select_shape (ConnectedRegions, SelectedRegions, 'area', 'and', 176, 9999999)  也就是說dual_threshold一條運算元頂這三條運算元。 dual_threshold運算元的缺陷:它只能分割出灰度值高的亮區域,不能分割出灰度值低的暗區域。  下面介紹var_threshold運算元。 2、var_threshold  先看var_threshold運算元的簽名: var_threshold(Image : Region : MaskWidth, MaskHeight, StdDevScale, AbsThreshold, LightDark : ) MaskWidth、 MaskHeight是用於濾波平滑的掩膜單元;StdDevScale是標準差乘數因子(簡稱標準差因子);AbsThreshold是設定的絕對閾值;LightDark有4個值可選,'light'、'dark'、'equal'、'not_equal'。  需要強調的是var_threshold運算元和dyn_threshold運算元極為類似。不同的是var_threshold整合度更高,並且加入了“標準差×標準差因子”這一變數。  舉例:   1 read_image (Image, 'C:/1.png') 2 var_threshold (Image, Region, 4, 4, 0.2, 12, 'dark')   在該程式中,先用4×4的掩膜在影象上逐畫素遊走,用原圖中的當前畫素和對應掩膜中16個畫素的灰度均值對比,找出暗(dark)的區域。當原影象素灰度比對應的掩膜灰度均值低(0.2,12)個灰階時,該區域被分割出來。本程式中StdDevScale = 0.2, AbsThreshold = 12,問題的關鍵就是理解如何通過StdDevScaleAbsThreshold來確定用於分割的閾值。 var_threshold的幫助文件中是這麼寫的:   說明: 1、d(x,y)指的是遍歷每個畫素時,掩膜覆蓋的那些畫素塊(本例中是4×4 = 16個畫素)灰度的標準差;StdDevScale 是標準差因子。 2、當標準差因子StdDevScale ≥ 0 時,v(x,y) 取(StdDevScale ×標準差)和AbsThreshold 中較大的那個。 3、當標準差因子StdDevScale < 0 時,v(x,y) 取(StdDevScale ×標準差)和AbsThreshold 中較小的那個。實測發現,這裡的比較大小是帶符號比較,由於標準差是非負數,當StdDevScale < 0 時,(StdDevScale ×標準差)≤ 0恆成立。所以此時的取值就是(StdDevScale ×標準差)。  文件是這麼說的:  If StdDevScale*dev(x,y) is below AbsThreshold for positive values of StdDevScale or above for negative values StdDevScale, AbsThreshold is taken instead.  大致意思是: 當StdDevScale為正時,如果StdDevScale*dev(x,y) 低於 AbsThreshold,則採用AbsThreshold。 當StdDevScale為負時,如果StdDevScale*dev(x,y) 高於 AbsThreshold,則採用AbsThreshold。  我找了一塊黑白過渡處4×4的畫素塊,求得它的灰度標準差為51.16(或49.53):    幫助文件中StdDevScale 的推薦值範圍是-1~1,一般通過上面的例子可知,一般的明顯的黑白過度處的標準差在50左右,乘以StdDevScale即-50 ~ 50 ,50的灰度差異,對於分割來說一般是夠了的。  文件還說:推薦的值是0.2,如果引數StdDevScale太大,可能分割不出任何東西;如果引數StdDevScale太小(例如-2),可能會把整個影象區域全部輸出,也就說達不到有效分割的目的。(……with 0.2 as a suggested value. If the parameter is too high or too low, an empty or full region may be returned.)  最後再看看是怎麼分割畫素的:   其中g(x,y)指的是原始影象當前畫素的灰度值;m(x,y)指的是遍歷畫素時,掩膜覆蓋的畫素的平均灰度值(mean)。  以LightDark = ‘dark’為例,當滿足m(x,y) - g(x,y) ≥ v(x,y)時(即原始影象對應畫素灰度比掩膜畫素灰度均值低v(x,y)個灰度值以上),相應的灰度值低的暗畫素被分割出來。  最後看幾個例子體會一下:(對比之前的例子var_threshold (Image, Region, 4, 4, 0.2, 12, 'dark')的效果)  ① 將AbsThreshold 由12改成30,此時分割出的區域變小。  1 read_image (Image, 'C:/1.png') 2 var_threshold (Image, Region, 4, 4, 0.2, 30, 'dark')   ② AbsThreshold 保持12不變,將StdDevScale由0.2改成0.7,此時分割出的區域變小。   ③ 將引數改為var_threshold (Image, Region, 4, 4, -0.01, 12, 'dark'),此時分割出的區域大大增加,由前面的分析可知,此時引數AbsThreshold = 12無效,事實上,此時將AbsThreshold 改為1、50甚至200都對最終結果沒有任何影響。 通過本人的分析,我認為StdDevScale取負值意義不大,因為它會分割出大量的不需要的區域,故一般推薦使用該運算元時,StdDevScale取正值。  需要強調的是:在黑白過渡處,一般掩膜覆蓋的畫素的標準差較大,而在其他平緩的地方,標準差較小;因此最終採用的分割閾值隨著掩膜在不斷遍歷畫素的過程中,在(StdDevScale×標準差)和AbsThreshold 之間不斷切換。 var_threshold和dyn_threshold的區別和聯絡: dyn_threshold是將原圖和濾波平滑後的圖對比,var_threshold是將原圖和對應畫素掩膜覆蓋的畫素的平均灰度值對比。  在運算元var_threshold中,如果引數StdDevScale = 0,那麼就可以用動態閾值的方式非常近似地模擬。以下兩種演算法的效果極為類似:  1 read_image (Image, 'C:/1.png') 2 var_threshold (Image, Region, 4, 4, 0, 12, 'dark')  1 read_image (Image, 'C:/1.png') 2 mean_image (Image, ImageMean, 4, 4) 3 dyn_threshold (Image, ImageMean, RegionDynThresh, 12, 'dark')  兩種方法的效果圖:   那麼當StdDevScale > 0 時,var_threshold對比dyn_threshold還存在什麼優點呢?我認為是在黑白過渡處能減少分割出不需要的區域的概率。(因為黑白過渡處標準差大,當然前提是StdDevScale 不能設定得太小)