(0)輪廓分析概述及作用
- 通過(guò)將
Canny邊緣提取
或者二值化結(jié)果
作為輸入圖像
來(lái)實(shí)現(xiàn)輪廓發(fā)現(xiàn)與繪制
废封,
可是這些并不是
我們想要的最終結(jié)果
,
我們一般根據(jù)獲取到的輪廓
求出它們的外接矩形
或者最小外接矩形
,
并計(jì)算外接矩形
的橫縱比例钮科、輪廓面積退盯、周長(zhǎng)等數(shù)據(jù)
,
然后使用這些數(shù)據(jù)
實(shí)現(xiàn)特定幾何形狀輪廓
的查找與過(guò)濾
撤师,
為后續(xù)的處理與分析
剔除不正確的區(qū)域
而保留候選對(duì)象
剂府。
(1)邊界框
- 最常見(jiàn)的獲取
輪廓的外接矩形
是邊界框
,
獲取每個(gè)輪廓的邊界框
丈氓,
通過(guò)它
可以得到與各個(gè)輪廓相對(duì)應(yīng)的高度與寬度
周循,
并能通過(guò)它計(jì)算出輪廓的縱橫比
。
通過(guò)輪廓點(diǎn)集合得到輪廓邊界框的API如下:
boundingRect(MatOfPoint points)
其中万俗,points
是輪廓所有點(diǎn)的集合對(duì)象
湾笛。注意其數(shù)據(jù)類型。
調(diào)用該API會(huì)返回一個(gè)Rect對(duì)象實(shí)例
闰歪,它是OpenCV關(guān)于矩形的數(shù)據(jù)結(jié)構(gòu)
嚎研,
從中可以得到外界矩形(邊界框)的寬高
,
然后就可以計(jì)算出輪廓的橫縱比
了库倘。
這種情況下得到的邊界框不一定滿足條件
临扮,有時(shí)候我們還需要獲取輪廓的最小邊界框
。
(2)最小邊界框
與上面邊界框不同
的是教翩,
獲取到的最小邊界框
有時(shí)候不是一個(gè)水平或者垂直的矩形
杆勇,
而是一個(gè)旋轉(zhuǎn)了一定角度的矩形
,
但是最小外接矩形(最小邊界框)
能夠更加真實(shí)地反映出輪廓的幾何結(jié)構(gòu)大小
饱亿,
而橫縱比結(jié)果
更能反映出輪廓的真實(shí)幾何特征
蚜退,
所以有些時(shí)候我們計(jì)算的經(jīng)常
是最小外接矩形
闰靴,
相關(guān)API函數(shù)如下:
RotatedRect minAreaRect(MatOfPoint2f points)
其中,points
是輪廓的所有點(diǎn)的集合對(duì)象钻注。注意其數(shù)據(jù)類型蚂且。
- 調(diào)用該API會(huì)返回一個(gè)
RotatedRect對(duì)象實(shí)例
,
它是OpenCV關(guān)于旋轉(zhuǎn)矩形的數(shù)據(jù)結(jié)構(gòu)
幅恋,
其包含了旋轉(zhuǎn)角度杏死,矩形的寬、高及四個(gè)頂點(diǎn)等信息
捆交,
通過(guò)相關(guān)的API
都可以查詢獲得
淑翼,
繪制旋轉(zhuǎn)矩形對(duì)象
的時(shí)候,
首先
需要得到四個(gè)頂點(diǎn)
零渐,
然后通過(guò)OpenCV繪制直線的API
來(lái)完成旋轉(zhuǎn)矩形的繪制
窒舟。
(3)面積與周長(zhǎng)
-
輪廓分析
中包含了輪廓大小的度量
,
這些度量最常見(jiàn)的就是計(jì)算輪廓的面積大小
與長(zhǎng)度大小
诵盼,
這些數(shù)據(jù)對(duì)分析輪廓
與過(guò)濾掉一些不符合條件的輪廓
十分有用惠豺。
計(jì)算輪廓面積
的API如下:
contourArea(Mat contour, boolean oriented)
contour
:輪廓的所有點(diǎn)
的集合對(duì)象
。
oriented
:表示輪廓的方向
风宁,當(dāng)oriented = true
時(shí)返回的面積
是一個(gè)有符號(hào)值
洁墙,默認(rèn)為false
,返回的是絕對(duì)值
戒财。
計(jì)算輪廓周長(zhǎng)
的API如下:
arcLength(MatOfPoint2f curve, boolean closed)
curve
:輪廓的所有點(diǎn)
的集合對(duì)象
热监。注意數(shù)據(jù)類型。
closed
:表示是否為閉合曲線
饮寞,默認(rèn)是true
孝扛。
完整的發(fā)現(xiàn)獲取輪廓、外接輪廓幽崩、最小外接輪廓苦始、橫縱比、面積與長(zhǎng)度
的代碼演示如下:
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);
// 輪廓發(fā)現(xiàn)
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));
// 測(cè)量輪廓
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);//一個(gè)輪廓元素打印一次
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);
}
// 釋放內(nèi)存
gray.release();
binary.release();
}
運(yùn)行結(jié)果(左側(cè)是原圖慌申,右側(cè)是輪廓發(fā)現(xiàn)與繪制陌选,計(jì)算結(jié)果參見(jiàn)logcat):上述的代碼是求取圖像的全部輪廓
,
修改
上述程序蹄溉,把返回輪廓改為返回最外層輪廓RETR_EXTERNAL
咨油,
同時(shí)修改閾值化方法
,將其改為THRESH_BINARY_INV
柒爵,
則運(yùn)行結(jié)果如下:
- 感興趣的小伙伴可以進(jìn)一步細(xì)化該方法役电,
將計(jì)算得到的輪廓幾何屬性值如長(zhǎng)度、面積等
通過(guò)putText函數(shù)顯示到輸出的圖像上
參考材料
- 《OpenCV Android 開(kāi)發(fā)實(shí)戰(zhàn)》(賈志剛 著)
- 關(guān)于《OpenCV Android 開(kāi)發(fā)實(shí)戰(zhàn)》作者的GitHub項(xiàng)目
- 筆者基于作者GitHub維護(hù)的APP