OpenCV獲取圖像輪廓信息
在計算機視覺領域,識別和分析圖像中的對象形狀是一項基本任務。OpenCV 庫提供了一個強大的工具——輪廓檢測(Contour Detection),它能夠幫助我們精確地定位對象的邊界。這篇博文將帶你入門 OpenCV 的輪廓檢測,理解其原理,并學會如何在 C++ 中使用它。
1.什么是輪廓?
簡單來說,輪廓可以看作是一條連接所有具有相同顏色或灰度值的連續點(沿著邊界)的曲線。
更準確地說,輪廓是圖像中強度或顏色發生顯著變化的區域的邊界。在 OpenCV 中,輪廓檢測通常作用于二值圖像(只有黑色和白色像素的圖像)。算法會尋找白色(或非零)區域的邊界,并將這些邊界表示為一系列點的坐標。
通過檢測輪廓,我們可以實現目標分割、形狀分析、物體計數、特征提取及對象檢測和識別等任務。
如上圖1,2,3 輪廓還有層次,有最外的輪廓1依次往里包括2,3輪廓。
2. OpenCV 輪廓檢測函數
2.1 findContours 函數
findContours
是 OpenCV 中最常用的輪廓檢測函數,其函數原型如下:
void findContours(InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy,int mode, int method, Point offset = Point());
- image:輸入的二值圖像(非零值表示前景)。
- contours:輸出輪廓,存儲為
std::vector<std::vector<Point>>
。 - hierarchy:輸出輪廓的層級信息,描述輪廓之間的嵌套關系。
- mode:輪廓檢索模式,如
RETR_EXTERNAL
(只檢測最外層輪廓)、RETR_LIST
(檢測所有輪廓但不建立層級關系)、RETR_TREE
(檢測所有輪廓并重構完整的層級結構)。 - method:輪廓近似方法,如
CHAIN_APPROX_SIMPLE
(壓縮水平、垂直和對角冗余點)和CHAIN_APPROX_NONE
(存儲所有點)。 - offset:可選參數,用于給輸出輪廓的所有點加上偏移值。
2.3 drawContours 函數
drawContours
函數用于在圖像上繪制檢測到的輪廓。它可以幫助直觀展示輪廓檢測和形狀分析的結果。函數原型如下:
void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx,const Scalar& color, int thickness = 1, int lineType = LINE_8,InputArray hierarchy = noArray(), int maxLevel = INT_MAX, Point offset = Point());
- image:輸入輸出圖像,即在該圖像上繪制輪廓。
- contours:輪廓集合,通常由
findContours
得到,類型為std::vector<std::vector<Point>>
。 - contourIdx:指定繪制哪一個輪廓;若設為 -1,則繪制所有輪廓。
- color:繪制輪廓的顏色,如
Scalar(0,255,0)
表示綠色。 - thickness:輪廓線的粗細;若為負值,則填充輪廓內部。
- lineType:線型,默認
LINE_8
。 - hierarchy:輪廓的層級信息,可選。
- maxLevel:繪制輪廓的最大層級,默認
INT_MAX
。 - offset:繪制時添加的偏移量。
drawContours
常用于可視化輪廓檢測結果,結合 findContours
使用可以將檢測到的目標區域在圖像上直觀標記出來。
2.3 輪廓檢測的基本步驟
使用 OpenCV C++ 進行輪廓檢測通常遵循以下步驟:
- 讀取圖像 (Read Image): 使用
cv::imread
加載你想要處理的源圖像到cv::Mat
對象。 - 轉換為灰度圖 (Convert to Grayscale): 使用
cv::cvtColor
將彩色圖像轉換為灰度圖,以簡化圖像信息。 - 二值化 (Thresholding): 使用
cv::threshold
將灰度圖像轉換為二值圖像。這是輪廓檢測的關鍵步驟,因為findContours
函數通常需要二值圖像作為輸入。 - 查找輪廓 (Find Contours): 使用
cv::findContours()
函數來檢測圖像中的所有輪廓。 - 繪制輪廓 (Draw Contours): (可選)使用
cv::drawContours()
函數將檢測到的輪廓繪制在原始圖像或新的畫布上,以便可視化。
2.4 參考代碼
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;int main() {// 讀取灰度圖像Mat src = imread("E:/image/test.png");if (src.empty()) {cerr << "圖像加載失敗!" << endl;return -1;}Mat gray;cvtColor(src, gray, COLOR_BGR2GRAY);GaussianBlur(gray, gray, Size(5, 5), 0);Mat binary;double otsu_thresh_val = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);cout << "Otsu 自動選擇的閾值為:" << otsu_thresh_val << endl;imshow("原始灰度圖像", gray);imshow("otsu 二值化", binary);//查找輪廓vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(binary, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);cout << hierarchy.size();//繪制所有輪廓Mat contourImage = src.clone();drawContours(contourImage, contours, -1, Scalar(0, 0, 255), 1);imshow("contour", contourImage);waitKey(0);destroyAllWindows();return 0;
}
如果只想要最外面的輪廓只需要把參數RETR_TREE改為**RETR_EXTERNAL
**
3. 計算并繪制輪廓幾何特征
在檢測到輪廓后,我們可以計算并繪制輪廓的幾何特征,例如:
- 面積 (
contourArea
) - 周長 (
arcLength
) - 重心
- 最小外接矩形 (
boundingRect
) - 最小外接圓 (
minEnclosingCircle
)
3.1 參考代碼
#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;int main() {// 讀取圖像并轉換為灰度圖Mat src = imread("E:/image/test1.png");if (src.empty()) {cerr << "圖像加載失敗!" << endl;return -1;}Mat gray;cvtColor(src, gray, COLOR_BGR2GRAY);// 應用閾值處理得到二值圖像Mat binary;threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);// 檢測輪廓vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);// 創建用于顯示輪廓的圖像Mat contourImage = src.clone();for (size_t i = 0; i < contours.size(); i++) {// 繪制輪廓drawContours(contourImage, contours, static_cast<int>(i), Scalar(0, 255, 0), 2);// 計算輪廓面積和周長double area = contourArea(contours[i]);double perimeter = arcLength(contours[i], true);if (area < 500) continue;// 計算重心Moments M = moments(contours[i]);int cx = static_cast<int>(M.m10 / M.m00);int cy = static_cast<int>(M.m01 / M.m00);circle(contourImage, Point(cx, cy), 5, Scalar(255, 0, 0), -1);// 計算外接矩形Rect boundingBox = boundingRect(contours[i]);rectangle(contourImage, boundingBox, Scalar(0, 0, 255), 2);//最小外接矩形RotatedRect box2 = minAreaRect(contours[i]);Point2f vertices[4];box2.points(vertices);for (int j = 0; j < 4; j++) {line(contourImage, vertices[j], vertices[(j + 1) % 4], Scalar(0, 255, 255), 1);}// 計算最小外接圓Point2f center;float radius;minEnclosingCircle(contours[i], center, radius);circle(contourImage, center, static_cast<int>(radius), Scalar(255, 255, 0), 2);// 在圖像上標注幾何特征putText(contourImage, "Area: " + to_string(static_cast<int>(area)),Point(boundingBox.x, boundingBox.y - 10), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 255), 1);putText(contourImage, "Perimeter: " + to_string(static_cast<int>(perimeter)),Point(boundingBox.x, boundingBox.y + boundingBox.height + 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 255), 1);}// 顯示結果imshow("輪廓檢測與幾何特征", contourImage);waitKey(0);return 0;
}
代碼簡介
- 輪廓檢測:使用
findContours
提取二值圖像中的所有輪廓。 - 繪制輪廓:使用
drawContours
繪制檢測到的輪廓。 - 計算幾何特征:
- 面積和周長:使用
contourArea
和arcLength
計算。 - 重心:通過
moments
計算輪廓的中心點,并用circle
繪制。 - 最小外接矩形:使用
boundingRect
計算,并用rectangle
繪制。 - 最小外接圓:使用
minEnclosingCircle
計算,并用circle
繪制。
- 面積和周長:使用
- 標注特征信息:使用
putText
在圖像上顯示面積和周長。 - 面積篩選:代碼中過濾不顯示面積小于500的輪廓
4. 應用場景
輪廓檢測技術在多個領域都有廣泛應用,包括但不限于:
-
目標分割和計數
利用輪廓檢測可以分割圖像中的獨立目標,并統計數量,如細胞計數、車牌識別等。
-
形狀分析
分析輪廓的幾何特征(如周長、面積、凸包、旋轉角度等),用于目標識別和匹配。
-
運動跟蹤
在視頻處理中,通過檢測運動目標的輪廓,進一步實現對象跟蹤和行為分析。
-
OCR 前處理
對文檔圖像進行輪廓檢測,提取文字區域,為光學字符識別(OCR)提供準備工作。