Emgu 圖像閾值
原文地址:http://www.cnblogs.com/CoverCat/p/5043833.html
轉載,備查
Visual Studio Community 2015 工程和代碼:http://pan.baidu.com/s/1o7lxYSM
內容
在這篇文章中將提到以下內容:
- 全局閾值
- 自適應閾值
- Otsu‘s二值化
在圖像處理中,會希望忽略掉一些灰度細節,只保留主體的輪廓,對灰度圖像進行閾值化處理能達到這個目的。
“其基本的思想是,給定一個數組和一個閾值,然後根據數組中的每個元素的值是低於還是高於閾值而進行一些處理”——《學習OpenCV(中文版)》,這裏說的“數組”即為圖像數據,
而“進行一些處理”說的是進行分類,只有兩類,根據值不同分到不同的類中。
準備工作
- 創建工程——參考Emgu學習之(一)——Emgu介紹創建一個名為“Threshold”WinForm項目
- 在Form1.cs中引用命名空間:
using Emgu.CV; using Emgu.CV.Structure; using Emgu.CV.CvEnum;
- 界面:在Form1中添加1行2列的TableLayoutPanel容器,然後添加2個Emgu.CV.UI.ImageBox控件(參考Emgu學習之(二)——圖像讀取、顯示、保存),添加後界面如下:
- 設置imageBox1和imageBox2的SizeMode屬性為StretchImage
全局閾值
全局閾值指的是整個圖像數據使用一個閾值進行篩選分類,OpenCV提供cvThreshold()方法進行閾值化操作,在Emgu中對應的方法名稱為Threshold。
Threshold方法接受一個類型為ThresholdType的參數,ThresholdType是一個枚舉類型,其枚舉值為:
Binary = 0,
BinaryInv = 1, Trunc = 2, ToZero = 3, ToZeroInv = 4, Mask = 7,//這裏不做介紹 Otsu = 8 //後面會做介紹
- Binary(二進制閾值化)——二進制閾值化是指將大於閥值的像素點設置為最大值,而小於閥值的像素點設置為0,即:
value = value > threshold ? max_value : 0
- BinaryInv(反向二進制閾值化)——與二進制閾值化正好相反,反向二進制閾值化是在像素點的值大於閥值時像素點設置為0,反之則設置為最大值,即:
value = value > threshold ? 0 : max_value
- Trunc(截斷閾值化)——截斷閾值化是指當像素點的值大於閥值時,像素點的值設置為閥值,反之則保留像素值本身,即:
value = value > threshold ? threshold : value
- ToZero(超閾值歸零化)——超閾值歸零化是指當像素點的值大於閥值時,像素點保留原值,反之則設置為0,即:
value = value > threshold ? value : 0
- ToZeroINV(低於閾值歸零化)——與超閾值歸零化相反,低於閾值歸零化是指當像素點的值大於閥值時,像素點值被設置為0,反之則保留像素點原值,即:
value = value > threshold ? 0 : value
從上邊描述我們可以看出二進制閾值化/反二進制閾值化需要一個max_value(最大值)的參數,同時,二進制閾值化/反二進制閾值化處理後圖像數據中只存在兩種
可能的值:0和max_value,這種圖像稱為二值化圖像。
“在數字圖像處理中,二值圖像占有非常重要的地位,首先,圖像的二值化有利於圖像的進一步處理,使圖像變得簡單,而且數據量減小,能凸顯出感興趣的目標的輪
廓。其次,要進行二值圖像的處理與分析,首先要把灰度圖像二值化,得到二值化圖像。所有灰度大於或等於閾值的像素被判定為屬於特定物體,其灰度值為255表示,
否則這些像素點被排除在物體區域以外,灰度值為0,表示背景或者例外的物體區域。”——百度百科
下面的代碼顯示了如何使用二進制閾值化處理,如果你要使用不是二進制閾值化處理,那麽max_value可以不用在意設什麽值。
{ using (var image = new Image<Bgr, Byte>(Properties.Resources.chess3)) { var grayImage = image.Convert<Gray, Byte>();//轉為灰度圖 var threshImage = grayImage.CopyBlank(); CvInvoke.Threshold( grayImage, threshImage, 150, //閥值 255, //最大值 ThresholdType.Binary);//二進制閾值化 imageBox1.Image = grayImage; imageBox2.Image = threshImage; } }
效果如下: Binary -> BinaryInv -> Trunc -> ToZero -> ToZeroInv
自適應閾值
全局閾值是整幅圖像使用一個閥值,這並不能適應所有的情況。自適應閾值是圖像的不同的區域使用不同的閥值,而閥值是對這個區域計算得來的,OpenCV提供cvAdaptiveThreshold()
函數進行自適應閾值化處理,這個函數提供兩種計算閥值的方法,分別為CV_ADAPTIVE_THRESH_MEAN_C和CV_ADAPTIVE_THRESH_GAUSSIAN_C。
“在這兩種情況下,自適應閾值T(x,y)在每個像素點都不同。通過計算像素點周圍的b x b區域的加權平均,然後減去一個常數來得到自適應閾值, b有參數block_size指定,常數有param1
指定。如果使用CV_ADAPTIVE_THRESH_MEAN_C方法,那麽對區域的所有像素平均加權。如果使用了CV_ADAPTIVE_THRESH_GAUSSIAN_C放,那麽區域中的(x,y)周圍的像素
根據高斯函數按照它們離中心點的距離進行加權計算。”——《學習OpenCV(中文版)》
Emgu中CVInvoke類提供了AdaptiveThreshold靜態方法進行自適應閾值處理,這個方法的原型為:
public static void AdaptiveThreshold( IInputArray src, //原圖像 IOutputArray dst, //結果圖像 double maxValue, //二進制閾值化/反二進制閾值化處理使用到的最大值 CvEnum.AdaptiveThresholdType adaptiveType, // 自適應閾值計算方式:MeanC或GaussianC CvEnum.ThresholdType thresholdType, //閾值化方式,必須為二進制閾值化和反二進制閾值化之一(Binary / BinaryInv) int blockSize, //計算使用的區域矩陣大小:3,5,7,9... double param1) //常數
同時,Image類也提供了一個封裝了AdaptiveThreshold方法的ThresholdAdaptive方法,下面的代碼中使用的幾位Image類的ThresholdAdaptive方法:
private void Form1_Load(object sender, EventArgs e) { var grayImage = new Image<Gray, Byte>(Properties.Resources.chess3); //CvInvoke.AdaptiveThreshold(grayImage, threshImage, 255, AdaptiveThresholdType.GaussianC, ThresholdType.BinaryInv, 12, 5); var threshImage = grayImage.ThresholdAdaptive( new Gray(255), AdaptiveThresholdType.MeanC, ThresholdType.Binary, 9, new Gray(5)); imageBox1.Image = grayImage; imageBox2.Image = threshImage; }
執行效果為:MeanC -> GaussianC
Otsu二值化
在全局閾值的代碼中,我們使用150作為二進制閾值化的閥值,但是這個閥值是我隨意選擇的,我並不確定這個閥值是否是合適的閾值。而如果使用Otsu二值化方法,它可以根據
圖像的直方圖計算出一個閥值。使用Otsu方法用到的是全局閾值提到過的Threshold方法,只是在傳入的ThresholdType值為Otsu,代碼如下:
var grayImage = new Image<Gray, Byte>(Properties.Resources.chess3); var threshImage = grayImage.CopyBlank(); CvInvoke.Threshold( grayImage, threshImage, 0, 255, ThresholdType.Otsu); imageBox1.Image = grayImage; imageBox2.Image = threshImage;
運行效果:
Emgu 圖像閾值