計算機視覺 OpenCV Android | 基本特徵檢測 之 輪廓分析
(0)輪廓分析概述及作用
-
通過將
Canny邊緣提取
或者二值化結果
作為輸入影象
來實現輪廓發現與繪製
,可是這些
並不是
我們想要的最終結果
,我們一般根據
獲取到的輪廓
求出它們的外接矩形
或者最小外接矩形
,並計算
外接矩形
的橫縱比例、輪廓面積、周長等資料
,然後
使用這些資料
實現特定幾何形狀輪廓
的查詢與過濾
,為
後續的處理與分析
剔除不正確的區域
而保留候選物件
。
(1)邊界框
最常見的獲取輪廓的外接矩形是邊界框,獲取每個輪廓的邊界框,通過它可以得到與各個輪廓相對應的高度與寬度,並能通過它計算出輪廓的縱橫比。
通過輪廓點集合得到輪廓邊界框的API函式與解釋如下:
boundingRect(MatOfPoint points)
其中,ponts是輪廓所有點的集合物件。
呼叫該API會返回一個Rect物件例項,它是OpenCV關於矩形的資料結構,從中可以得到外界矩形(邊界框)的寬高,然後就可以計算出輪廓的橫縱比了。這種情況下得到的邊界框不一定滿足條件,有時候我們還需要獲取輪廓的最小邊界框。
(2)最小邊界框
與上面邊界框不同的是,獲取到的最小邊界框有時候不是一個水平或者垂直的矩形,而是一個旋轉了一定角度的矩形,但是最小外接矩形(最小邊界框)能夠更加真實地反映出輪廓的幾何結構大小,而橫縱比結果更能反映出輪廓的真實幾何特徵,所以有些時候我們計算的經常是最小外接矩形,它的API函式與相關引數的解釋如下:
RotatedRect minAreaRect(MatOfPoint2f points)
其中,ponts是輪廓的所有點的集合物件。
呼叫該API會返回一個RotatedRect物件例項,它是OpenCV關於旋轉矩形的資料結構,其包含了旋轉角度,矩形的寬、高及四個頂點等資訊,通過相關的API都可以查詢獲得,繪製旋轉矩形物件的時候,首先需要得到四個頂點,然後通過OpenCV繪製直線的API來完成旋轉矩形的繪製。
(3)面積與周長
輪廓分析中包含了輪廓大小的度量,這些度量最常見的就是計算輪廓的面積大小與長度大小,這些資料對分析輪廓與過濾掉一些不符合條件的輪廓十分有用。計算輪廓面積的API函式與引數解釋如下:
contourArea(Mat contour, boolean oriented)
·ponts:輪廓的所有點的集合物件。
·oriented:表示輪廓的方向,當oriented=true時返回的面積是一個有符號值,預設為false,返回的是絕對值。
計算長度的API函式與引數解釋如下:
arcLength(MatOfPoint2f curve, boolean closed)
·curve:輪廓的所有點的集合物件。
·closed:表示是否為閉合曲線,預設是true。
完整的分析輪廓、獲取輪廓、外接輪廓、最小外接輪廓、橫縱比、面積與長度的程式碼演示如下:
private void measureContours(Mat src, Mat dst) {
Mat gray= new Mat();
Mat binary = new Mat();
// 二值
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(gray, binary, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
// 輪廓發現
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(binary, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0, 0));
// 測量輪廓
dst.create(src.size(), src.type());
for(int i=0; i<contours.size(); i++) {
Rect rect = Imgproc.boundingRect(contours.get(i));
double w = rect.width;
double h = rect.height;
double rate = Math.min(w, h)/Math.max(w, h);
Log.i("Bound Rect", "rate:" + rate);
RotatedRect minRect = Imgproc.minAreaRect(new MatOfPoint2f(contours.get(i).toArray()));
w = minRect.size.width;
h = minRect.size.height;
rate = Math.min(w, h)/Math.max(w, h);
Log.i("Min Bound Rect", "rate:" + rate);
double area = Imgproc.contourArea(contours.get(i), false);
double arclen = Imgproc.arcLength(new MatOfPoint2f(contours.get(i).toArray()), true);
Log.i("contourArea", "area:" + rate);
Log.i("arcLength", "arcLength:" + arclen);
Imgproc.drawContours(dst, contours, i, new Scalar(0, 0, 255), 1);
}
// 釋放記憶體
gray.release();
binary.release();
}
上面演示程式碼的執行結果如圖5-6所示(圖5-6a是原圖,圖5-6b是輪廓發現與繪製,計算結果請參見logcat)。

上述的程式碼是求取影象的全部輪廓,修改上述程式把返回輪廓改為返回最外層輪廓RETR_EXTERNAL,同時修改閾值化方法,將其改為THRESH_BINARY_INV,則執行結果如圖5-7所示。

-
感興趣的小夥伴可以進一步細化該方法,
將計算得到的輪廓幾何屬性值如長度、面積等
通過putText函式顯示到輸出的影象上;
參考材料
- 《OpenCV Android 開發實戰》(賈志剛 著)
- 關於《OpenCV Android 開發實戰》作者的GitHub專案
- 筆者基於作者GitHub維護的APP