輪廓檢測
- 一、輪廓檢測
- 二、輪廓的層級
- 三、輪廓的特征
- 3.1、輪廓面積
- 3.2、輪廓周長
- 3.3、邊界矩形
- 3.4、最小外接圓
- 3.5、近似輪廓
- 3.6、凸包
一、輪廓檢測
輪廓可以簡單的描述為具有相同顏色或灰度的連續點連在一起的一條曲線,輪廓通暢會顯示出圖像中物體的形狀。關于輪廓的檢測,最容易想到的辦法是跟蹤檢測的邊緣點,從而找出閉合的輪廓線。但是如果把這個思路付諸行動就會發現情況并非想象的那么簡單,因為邊緣往往在梯度很弱的地方消失,而且輪廓線有時會有多個分支。實際上,邊緣線中很少存在完美的輪廓線,普遍的情況是包含很多細小的、不連續的輪廓片段。
//在二值圖像中尋找輪廓
void Imgproc.findContours(Mat image, List<MatOfPoint> contours, Mat hierarchy, int mode, int method)
- image:輸入圖像,必須是8位單通道二值圖或灰度圖。如果是灰度圖,像素值為0的仍視為0,而像素值不為0的視作1,如此灰度圖也可作為二值圖處理
- contours:檢測到的輪廓
- hierarchy:輪廓的層級,包含了對輪廓之間的拓撲關系的描述。Hierarchy中的元素數量和輪廓中的元素數量是一致的。第i個輪廓contours[i]有著相對應的4個hierarchy索引,分別是hierarchy[i][0]、hierarchy[i][1]、hierarchy[i][2]和hierarchy[i][3]。它們分別是輪廓的同層下一個輪廓索引、同層上一個輪廓索引、第1個子輪廓索引和父輪廓索引。如果第i個輪廓沒有下一個同層輪廓、子輪廓或父輪廓,則對應的索引用負數表示。
- mode:輪廓提取模式,具體如下
- Imgproc.RETR_EXTERNAL:只檢測最外層輪廓,所有輪廓的hierarchy[i][2]和hierarchy[i][3]均設為-1
- Imgproc.RETR_LIST:檢測所有的輪廓,但輪廓之間不建立層級關系
- Imgproc.RETR_CCOMP:檢測所有的輪廓,所有輪廓建立一個樹形層級結構
- method:輪廓逼近方法,可選參數如下:
- Imgproc.CHAIN_APPROX_NONE:存儲所有輪廓點,兩個相鄰的輪廓點(x1, y1)和(x2, y2)必須是8連通,即max(abs(x1-x2), abs(y2-y1))=1
- Imgproc.CHAIN_APPROX_SIMPLE:壓縮水平方向、垂直方向和對角線方向的線段,只保存線段的端點
- Imgproc.CHAIN_APPROX_TC89_L1:使用teh-Chin1 chain近似算法中的L1算法
- Imgproc.CHAIN_APPROX_TC89_KCOS:使用teh-Chin1 chain近似算法中的KCOS算法
二、輪廓的層級
在 findContours()函數中有一個重要的參數 hierarchy,即輪廓的層級,它包含了對輪廓之間拓撲關系的描述。輪廓的層級是針對每個輪廓的,如果一張圖像有100個輪廓,則hierarchy 就有 100 項,其中第i項為 hierarchy[i]。如果輪廓 A 被另外一個輪 B 包圍,則AB 之間就是父子關系,其中 B輪廓為父輪廓,A 輪廓為子輪廓。父輪廓的層級比子輪廓高級,子輪廓還可以有子輪廓。下面用一個實例說明輪的層級關系。
如圖所示,這個嵌套的圖形中有6個輪廓,分別標記為 1~6號,它們之間有層級關系,其中1號輪廓在最外面,它的層級比其他的輪廓都要高。1號輪有3個子輪,分別為 2、3 和 5號輪廓,其中3號輪廓又有4號子輪廓,5 號輪廓又有6號子輪廓。
有6個輪廓的圖像:
輪廓的層級結構:
為了標識輪廓之間的關系,每個輪廓的Hierarchy[i]都是包含4個值的數組,這4個數組值標記為hierarchy[i][0]~hierarchy[i][3],它們的含義一次為Next、Previous、First_child和Parent:
- Next:表示同一層次的下一個輪廓的編號,如2號輪廓的Next是3號輪廓
- Previous:表示同一層次上的前一個輪廓編號,如3號輪廓的Previous是2號輪廓
- First_Child:表示第1個子輪廓的編號,如1號輪廓的First_Child是2號輪廓
- Parent:表示父輪廓編號,如5號輪廓的Parent是1號輪廓
某些輪廓是沒有子輪廓、父輪廓或同一層次的下一個輪廓的,這時用-1 表示。現在可以用這個數組來描述輪廓的結構了。例如,2號輪廓可以表示為[3,-1,-1,1],因為它只有 Next (3 號輪廓,用3表示)和 Parent(1號輪廓,用1表示),沒有 Previous 和 First Child(用-1 表示)。同理,5 號輪廓可以表示為[-1,3,6,1]。
public class ContourHierarchy {static {OpenCV.loadLocally(); // 自動下載并加載本地庫}public static void main(String[] args) {//讀取圖像并轉換為二值圖像 并顯示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo2/contour.png", Imgcodecs.IMREAD_GRAYSCALE);Mat binary = new Mat();Imgproc.threshold(src, binary, 90, 255, Imgproc.THRESH_BINARY);HighGui.imshow("binary", binary);HighGui.waitKey(0);//根據二值圖像檢測輪廓List<MatOfPoint> contour = new ArrayList<>();Mat hierarchy = new Mat();Imgproc.findContours(binary, contour, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);//畫出輪廓圖并顯示Mat imgCanny = new Mat(src.height(), src.width(), CvType.CV_8UC3, new Scalar(255, 255, 255));for (int i = 0; i < contour.size(); i++) {Imgproc.drawContours(imgCanny, contour, i, new Scalar(0, 0, 0), 1);}HighGui.imshow("Contours", imgCanny);HighGui.waitKey(0);//控制臺輸出hierarchy層級數據for (int i = 0; i < contour.size(); i++) {double[] d = hierarchy.get(0, i);for (int j = 0; j < d.length; j++) {System.out.print(d[j] + ",");}System.out.println();}System.exit(0);}
}
樹形結構:第一行為0號輪廓是1號輪廓的父級,共7個輪廓。
-1.0,-1.0,1.0,-1.0,
-1.0,-1.0,2.0,0.0,
3.0,-1.0,-1.0,1.0,
5.0,2.0,4.0,1.0,
-1.0,-1.0,-1.0,3.0,
-1.0,3.0,6.0,1.0,
-1.0,-1.0,-1.0,5.0,
原圖:
輪廓圖:
在檢測出輪廓之后,還需要drawContours()函數將輪廓繪制出來:
//繪制輪廓或輪廓內部
void Imgproc.drawContours(Mat image, List<MatOfPoint> contours, int contourIdx, Scalar color, int thickness)
- image:用于繪制輪廓的圖像
- contours:輸入的輪廓數據
- contourIdx:要繪制的輪廓的索引,如為負數,則繪制所有輪廓
- color:繪制輪廓的顏色
- thickness:繪制輪廓的線條粗細。如為負數,則繪制輪廓內部;如為1,則繪制該輪廓及嵌套的輪廓;如為2,則繪制該輪廓、嵌套的輪廓及嵌套輪廓的輪廓;其余以此類推。此參數僅當輪廓數據中含有層級數據時有效
public class FindContours {static {OpenCV.loadLocally(); // 自動下載并加載本地庫}public static void main(String[] args) {//讀取圖像并轉換為二值圖像 并顯示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/butterfly.png", Imgcodecs.IMREAD_GRAYSCALE);Mat binary = new Mat();Imgproc.threshold(src, binary, 90, 255, Imgproc.THRESH_BINARY);HighGui.imshow("binary", binary);HighGui.waitKey(0);//根據二值圖檢測輪廓List<MatOfPoint> contours = new ArrayList<>();Imgproc.findContours(binary, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);//畫出輪廓圖并顯示Mat imgBinary = new Mat(src.height(), src.width(), CvType.CV_8UC3, new Scalar(255, 255, 255));for (int i = 0; i < contours.size(); i++) {Imgproc.drawContours(imgBinary, contours, i, new Scalar(0, 0, 0), 1);}HighGui.imshow("Contours from Binary", imgBinary);HighGui.waitKey(0);//進行Canny邊緣檢測并顯示Mat canny = new Mat();Imgproc.Canny(src, canny, 60, 200);HighGui.imshow("Canny", canny);HighGui.waitKey(0);//根據Canny結果檢測輪廓List<MatOfPoint> contour2 = new ArrayList<>();Imgproc.findContours(canny, contour2, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);//畫出輪廓并顯示Mat imgCanny = new Mat(src.height(), src.width(), CvType.CV_8UC3, new Scalar(255, 255, 255));for (int i = 0; i < contour2.size(); i++) {Imgproc.drawContours(imgCanny, contour2, i, new Scalar(0, 0, 0), 1);}HighGui.imshow("Contours from Canny", imgCanny);HighGui.waitKey(0);System.exit(0);}
}
由于Canny邊緣檢測的輸出質量較高,其相應的輪廓圖也更清晰一些,二值圖生成的輪廓則有一些干擾。
二值圖:
二值圖的輪廓:
Canny邊緣圖:
Canny邊緣圖繪制的輪廓圖:
三、輪廓的特征
獲取輪廓后可以利用輪廓的各種特征來區分和識別物體。輪廓的特征包括面積、周長、邊界矩形、近似輪廓的凸包等。
3.1、輪廓面積
//計算輪廓面積
double Imgproc.contourArea(Mat contour, boolean oriented)
- contour:輪廓頂點數據
- oriented:面積是否具有方向性的標志。如為true,則函數返回有符號的面積值。面積是否帶符號取決與方向為順時針還是逆時針。默認情況下,此標志為false,函數返回面積的絕對值。
public class ContourArea {static {OpenCV.loadLocally(); // 自動下載并加載本地庫}public static void main(String[] args) {//讀取圖像并轉換為二值圖像 并顯示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo2/contour.png", Imgcodecs.IMREAD_GRAYSCALE);Mat binary = new Mat();Imgproc.threshold(src, binary, 90, 255, Imgproc.THRESH_BINARY);HighGui.imshow("binary", binary);HighGui.waitKey(0);//根據二值圖檢測輪廓List<MatOfPoint> contour = new ArrayList<>();Mat hierarchy = new Mat();Imgproc.findContours(binary, contour, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);//輸出各輪廓面積for (int i = 0; i < contour.size(); i++) {double d = Imgproc.contourArea(contour.get(i));System.out.println(i + ":" + d);}System.exit(0);}
}
二值圖:
面積:
0:121801.0
1:56306.0
2:1998.0
3:1979.0
4:306.0
5:1979.0
6:306.0
3.2、輪廓周長
//計算輪廓周長或曲線長度
double Imgproc.arcLength(MatOfPoint2f curve, boolean closed)
- curve:輪廓或曲線的數據
- closed:曲線是否閉合的標志
public class ArcLength {static {OpenCV.loadLocally(); // 自動下載并加載本地庫}public static void main(String[] args) {//讀取圖像并轉換為二值圖像 并顯示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo2/contour.png", Imgcodecs.IMREAD_GRAYSCALE);Mat binary = new Mat();Imgproc.threshold(src, binary, 90, 255, Imgproc.THRESH_BINARY);HighGui.imshow("binary", binary);HighGui.waitKey(0);//根據二值圖檢測輪廓List<MatOfPoint> contour = new ArrayList<>();Mat hierarchy = new Mat();Imgproc.findContours(binary, contour, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);//計算各個輪廓的周長并在控制臺輸出MatOfPoint2f dst = new MatOfPoint2f();for (int i = 0; i < contour.size(); i++) {contour.get(i).convertTo(dst, CvType.CV_32F);double d = Imgproc.arcLength(dst, true);System.out.println(i + ":" + Math.round(d));}System.exit(0);}
}
原圖:
周長:
0:1396
1:887
2:181
3:173
4:66
5:173
6:66
3.3、邊界矩形
邊界矩形有直邊界矩形(沒有旋轉的矩形)和最小外接矩形(旋轉的邊界矩形)兩種,OpenCV中分別用 boundingRect()
函數和 minAreaRect()
函數獲取這兩種邊界矩形。用 boundingRect()
函數找到的邊界矩形是不經過旋轉的,因此不是面積最小的矩形,用minAreaRect()
函數找到的邊界矩形才是面積最小的。直邊界矩形和最小外接矩形的區別如圖 9-17 所示。圖中傾斜的矩形是最小外接矩形,沒有傾斜的矩形是直邊界矩形。很明顯,最小外接矩形的面積比直邊界矩形要小得多。
//計算一個二維點集或灰度圖中非零像素的邊界矩形
Rect Imgproc.boundingRect(Mat array)
- array:輸入的二維點集或灰度圖
public class RoundingRect {static {OpenCV.loadLocally(); // 自動下載并加載本地庫}public static void main(String[] args) {//讀取圖像并轉換為二值圖像 并顯示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo2/contour.png", Imgcodecs.IMREAD_GRAYSCALE);Mat binary = new Mat();Imgproc.threshold(src, binary, 90, 255, Imgproc.THRESH_BINARY);HighGui.imshow("binary", binary);HighGui.waitKey(0);//根據二值圖檢測輪廓List<MatOfPoint> contour = new ArrayList<>();Mat hierarchy = new Mat();Imgproc.findContours(binary, contour, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);//在圖像上繪制各輪廓的邊界圖像src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo2/contour.png", Imgcodecs.IMREAD_GRAYSCALE);for (int i = 0; i < contour.size(); i++ ) {Rect rect = Imgproc.boundingRect(contour.get(i));Imgproc.rectangle(src, new Point(rect.x, rect.y), new Point(rect.x+rect.width, rect.y+rect.height), new Scalar(0, 0, 255), 3);}//在屏幕顯示HighGui.imshow("Rect", src);HighGui.waitKey(0);System.exit(0);}
}
二值圖:
直邊界矩形的圖像;
OpenCV中獲取最小外接矩形的函數圓形如下;
//尋找輸入二維點集的最小外接矩形
RotatedRect Imgproc.minAreaRect(MatOfPoint2f points)
- points:輸入的二維點集
該函數的返回值類型是旋轉矩形,即RotatedRect類,該類常用的成員變量有center、width、height和angle,如下圖:
但是根據這些數值把這個旋轉矩形畫出來比較繁瑣,為此需要用boxPoints()函數獲取旋轉矩形的4個頂點:
//獲取旋轉矩形的4個頂點
void Imgproc.boxPoints(RotatedRect box, Mat points)
- box:輸入的旋轉矩形
- points:輸出的4個頂點
public class MinAreaRect {static {OpenCV.loadLocally(); // 自動下載并加載本地庫}public static void main(String[] args) {//讀取圖像并轉換為二值圖像 并顯示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/seed.png", Imgcodecs.IMREAD_GRAYSCALE);Mat binary = new Mat();Imgproc.threshold(src, binary, 90, 255, Imgproc.THRESH_BINARY);HighGui.imshow("binary", binary);HighGui.waitKey(0);//根據二值圖檢測輪廓List<MatOfPoint> contour = new ArrayList<>();Mat hierarchy = new Mat();Imgproc.findContours(binary, contour, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);//重新后區彩色圖像,用于繪制最小外接矩形src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/seed.png", Imgcodecs.IMREAD_GRAYSCALE);//參數準備MatOfPoint2f dst = new MatOfPoint2f();Mat pts = new Mat();Scalar red = new Scalar(0, 0, 255);float[] data = new float[8];//用于獲取點集數據for (int n = 0; n < contour.size(); n++) {//將輪廓數據轉換為MatOfPoint2fcontour.get(n).convertTo(dst, CvType.CV_32F);//獲取最小外接矩形(旋轉矩形)RotatedRect rect = Imgproc.minAreaRect(dst);//獲取旋轉矩形的4個頂點Imgproc.boxPoints(rect, pts);pts.get(0, 0, data);//將4個頂點轉換為Point類Point pt1 = new Point(data[0], data[1]);Point pt2 = new Point(data[2], data[3]);Point pt3 = new Point(data[4], data[5]);Point pt4 = new Point(data[6], data[7]);//繪制最小外接矩形的4條邊Imgproc.line(src, pt1, pt2, red, 2);Imgproc.line(src, pt2, pt3, red, 2);Imgproc.line(src, pt3, pt4, red, 2);Imgproc.line(src, pt4, pt1, red, 2);}//顯示HighGui.imshow("MinAreaRect", src);HighGui.waitKey(0);System.exit(0);}
}
二值化圖像:
繪有最小外接矩形的圖像:
3.4、最小外接圓
有些情況下需要獲得一個對象的最小外接圓,這是需要用到minEncloingCircle()函數:
//尋找包圍點集的最小面積的圓
void Imgproc.minEnclosingCircle(MatOfPoint2f points, Point center, float[] radius)
- points:輸入的二維點集
- center:輸出圓的圓心
- radius:輸出圓的半徑
public class MinCircle {static {OpenCV.loadLocally(); // 自動下載并加載本地庫}public static void main(String[] args) {//讀取圖像并轉換為二值圖像 并顯示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/seed.png", Imgcodecs.IMREAD_GRAYSCALE);Mat binary = new Mat();Imgproc.threshold(src, binary, 90, 255, Imgproc.THRESH_BINARY);HighGui.imshow("binary", binary);HighGui.waitKey(0);//根據二值圖檢測輪廓List<MatOfPoint> contour = new ArrayList<>();Mat hierarchy = new Mat();Imgproc.findContours(binary, contour, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);//重新后區彩色圖像,用于繪制最小外接矩形src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/seed.png", Imgcodecs.IMREAD_GRAYSCALE);//參數準備Point center = new Point();float[] radius = new float[3];Scalar red = new Scalar(0, 0, 255);MatOfPoint2f dst = new MatOfPoint2f();for (int i = 0; i < contour.size(); i++) {//將輪廓數據轉換為MatOfPoint2fcontour.get(i).convertTo(dst, CvType.CV_32F);//獲取最小外接圓Imgproc.minEnclosingCircle(dst, center, radius);//繪制最小外接圓int r = Math.round(radius[0]);Imgproc.circle(src, center, r, red, 2);}//顯示HighGui.imshow("MinCircle", src);HighGui.waitKey(0);System.exit(0);}
}
二值圖:
繪有最下外接圓的圖像:
3.5、近似輪廓
有時用矩陣或圓形逼近物體輪廓差異會較大,此時可用多邊形逼近輪廓。OpenCV中獲取逼近輪廓的多邊形的函數:
//尋找逼近輪廓的多邊形(曲線)
void Imgproc.approxPolyDP(MatOfPoint2f curve, MatOfPoint2f approxCurve, double epsilon, boolean closed)
- curve:輸入的二維點集
- approxCurve:逼近的結果,類型應與輸入匹配
- epsilon:逼近的精度,即原始曲線和逼近多邊形(曲線)的最大距離
- closed:如為true,則表示逼近曲線為閉合曲線(最后一個頂點和第1個頂點相連),否則為不閉合
public class ApproxPolyDP {static {OpenCV.loadLocally(); // 自動下載并加載本地庫}public static void main(String[] args) {//讀取圖像并轉換為二值圖像 并顯示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/seed.png", Imgcodecs.IMREAD_GRAYSCALE);Mat binary = new Mat();Imgproc.threshold(src, binary, 90, 255, Imgproc.THRESH_BINARY);HighGui.imshow("binary", binary);HighGui.waitKey(0);//根據二值圖檢測輪廓List<MatOfPoint> contour = new ArrayList<>();Mat hierarchy = new Mat();Imgproc.findContours(binary, contour, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);//重新后區彩色圖像,用于繪制最小外接矩形src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/seed.png", Imgcodecs.IMREAD_GRAYSCALE);//參數準備Scalar red = new Scalar(0, 0, 255);MatOfPoint2f mop = new MatOfPoint2f();MatOfPoint2f dst = new MatOfPoint2f();//繪制逼近輪廓的多邊形for (int i = 0; i < contour.size(); i++) {//將輪廓數據轉換為MatOfPoint2fcontour.get(i).convertTo(mop, CvType.CV_32F);//輪廓面積太小的跳過不畫double area = Imgproc.contourArea(contour.get(i));if (area < 100) {continue;}//獲取逼近輪廓的多邊形Imgproc.approxPolyDP(mop, dst, 4, true);//將多邊形的數據轉換成數組以便于畫圖int row = dst.rows();float[] data = new float[row * 2];dst.get(0, 0, data);//畫多邊形的邊for (int j = 0; j < row - 1; j++) {Point pt1 = new Point(data[j * 2], data[j * 2 + 1]);Point pt2 = new Point(data[j * 2 + 2], data[j * 2 + 3]);Imgproc.line(src, pt1, pt2, red, 2);}//連接多邊形第1個和最后1個頂點Imgproc.line(src, new Point(data[0], data[1]), new Point(data[row * 2 - 2], data[row * 2 - 1]), red, 2);}//顯示HighGui.imshow("ApproxPolyDP", src);HighGui.waitKey(0);System.exit(0);}
}
二值圖:
繪有近似輪廓的圖像:
3.6、凸包
有些物體的形狀用多邊形逼近仍然不理想,如人手,此時可以利用凸包來近似。所謂凸包是指將最外圍的點連接起來構成凸邊形,如下圖:
//尋找點集的凸包
void Imgproc.convexHull(MatOfPoint points, MatOfInt hull, boolean clockwise)
- points:輸入的二維點集
- hull:輸出的凸包頂點
- clockwise:方向標志,如為true,則表示凸包為順時針方向,否則為逆時針方向。此處假設坐標系系統的x軸指向右方,y軸指向上方
public class ConvexHull {static {OpenCV.loadLocally(); // 自動下載并加載本地庫}public static void main(String[] args) {//讀取圖像并轉換為二值圖像 并顯示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo2/palm.png", Imgcodecs.IMREAD_GRAYSCALE);Mat binary = new Mat();Imgproc.threshold(src, binary, 230, 255, Imgproc.THRESH_BINARY);HighGui.imshow("binary", binary);HighGui.waitKey(0);//根據二值圖檢測輪廓List<MatOfPoint> contour = new ArrayList<>();Imgproc.findContours(binary, contour, new Mat(), Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);//重新后區彩色圖像,用于繪制最小外接矩形src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo2/palm.png");//參數準備MatOfInt onehull = new MatOfInt();List<MatOfPoint> hulls = new ArrayList<>();MatOfPoint c = new MatOfPoint();//繪制凸包for (int i = 0; i < contour.size(); i++) {//輪廓面積太小的跳過不畫c = contour.get(i);//第i個輪廓double area = Imgproc.contourArea(c);if (area < 100) {continue;}//獲取凸包,并將索引值轉換為點的坐標onehull為索引值Imgproc.convexHull(c, onehull);hulls.add(indexToPoint(c, onehull));}//繪制凸包for (int i = 0; i < hulls.size(); i++) {Imgproc.drawContours(src, hulls, i, new Scalar(0, 0, 255), 2);}//顯示HighGui.imshow("ConvexHull", src);HighGui.waitKey(0);System.exit(0);}//將輪廓的索引值轉換為點坐標的子程序public static MatOfPoint indexToPoint(MatOfPoint contour, MatOfInt index) {//將兩個參數轉換為數組類型int[] ind = index.toArray();Point[] con = contour.toArray();//獲取點的坐標Point[] pts = new Point[ind.length];for (int i = 0; i < ind.length; i++) {pts[i] = con[ind[i]];}//將點的坐標轉換成MatOfPoint數據類型MatOfPoint hull = new MatOfPoint();hull.fromArray(pts);return hull;}
}
二值化圖像:
繪有凸包的圖像: