一 Contour Finding
? ? Contours使用 STL-style vector<> 表示,如 vector<cv::Point>, vector<cv::Point2f>。opencv中,使用函數 cv::findContours() 尋找contours, 具體函數定義如下:
? ? void cv::findContours(cv::InputOutputArray image,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? cv::OutputArrayofArrays contours,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? cv::OutputArray hierarchy,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? int mode,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? int method,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? cv::Point offset = cv::Point());
? ?? void cv::findContours(cv::InputOutputArray image,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? cv::OutputArrayofArrays contours,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? int mode,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? int method,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? cv::Point offset = cv::Point());
? ? 參數 image 為8位單通道輸入圖像,一般情況下,該圖像可能由 cv::threshold(), cv::adaptiveThreshold 生成。當 image 由 cv::Canny() 生成時,cv::findContours() 僅當邊緣圖像為寬度為1的細區域圖像,對于閉合邊緣,可以使用內邊緣或者外邊緣代替邊緣圖像進行后續分析;但對于非閉合邊緣,個人認為?cv::Canny() 生成的邊緣圖像不適合使用?cv::findContours() 進行查找;替代方案是使用邊緣跟蹤算法,將?cv::Canny()? 生成邊緣保存在?vector<cv::Point> 中,然后使用 Contours 相關分析進行更多分析。
? ? 參數?contours 為 vector<vector<cv::Point>>,?vector<vector<cv::Point2f>>, 使用 array of arrays 結構可以同時保存多條 contours。
? ? 參數 hierarchy 為 vector<cv::Vec4i>, 每個元素對應一個 contour, 元素中4個整數表示該 contour 與其他 contour 之間的關系,具體關系如下:
? ? ? ??
? ? 其中, c 表示 contour, h 表示? hole.
? ? opencv 如何生成 contour 的 hierarchy? 個人認為(暫時沒有研究 opencv 具體實現),一種可行的思路是使用區域增長,大概流程如下:
? ? 1)將種子點設置在圖像邊角處,使用背景顏色進行區域增長,區域增長在前景處停止;
? ? 2)使用形態學算子對當前增長后區域進行 dilate 操作,使 c0, c1,... 層級上的 contours 包含在當前增長區域中;
? ? 3)繼續使用前景對圖像增長,將 h00, h01,..., h10, h11,... 等層級上的 contours 包含在當前增長區域中;
? ? 4)如此循環,即可生成 contour 的? hierarchy 結構。
? ? 要使用?vector<cv::Vec4i> 表示 以上樹形結構,每個?cv::Vec4i 元素值含義如下:
? ? 1) index 0: next contour(same level);
? ? 2) index 1: privous contour(same level);
? ? 3) index 2: first child(next level down);?
? ? 4) index 3: parent(next level up);
?? 參數 mode 表示如何提取 contours, 具體如下:
? ? 1) cv::PETR_EXTERNAL: 僅提取最外層 contour, 在以上圖例中,僅提取 c0;
? ? 2) cv::PETR_LIST: 提取所有 contours, 組成鏈表結構,在圖例中為: c010->c001->c000->h01->h00->c0;
? ? 3) cv::PETR_CCOMP: 提取所有 contours, 根據類型組成? hole list 和 contour list, 在圖例中為:c010->c001->c000->c0, h01->h00, c0->h01(此處將前兩個鏈表連接起來);
? ? 4) cv::PETR_TREE: 提取所有 contours, 建立樹形結構。
? ? 參數 method 對 contour 壓縮,以減少數據量,包括:
? ? 1) cv::CHAIN_APPROX_NONE: 不做任何壓縮;
? ? 2) cv::CHAIN_APPROX_SIMPLE: 壓縮水平,垂直,對角方向上的線段;
? ? 4) cv::CHAIN_APPROX_TC89_L1/cv::CHAIN_APPROX_TC89_KCOS: 使用曲線曲率信息做更復雜的壓縮。
? ? 參數 offset 將 contour 平移,在使用ROI進行 contour 提取時, 通過設置不同 offset 可以很方便的將 contour 繪制在原圖上。
二? Contour Drawing
? ? 使用 cv::drawContours() 在圖像上繪制 contours, 具體定義如下:
? ? void cv::drawContours(cv::InputOutputArray image,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? cv::InputArrayofArrays contours,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? int contourIdx,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? const cv::Scalar& color,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? int thickness = 1,?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? int lineType = 8,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? cv::InputArray hierarchy = noArray(),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? int maxLevel = INT_MAX(),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? cv::Point offset = cv::Point());
? ? 參數?image 為需要繪制 contours 圖像。
? ? 參數 contours 為?cv::findContours() 生成。
? ? 參數?contourIdx 選擇需要繪制的 contour, 如果其值為 -1, 則繪制所有 contours。
? ? 參數 color thickness lineType 分別定義線條顏色,線條寬度,線條類型(4連接 cv::LINE_4, 8連接 cv::LINE_8, 反走樣cv::LINE_AA)。
? ? 參數 hierarchy maxLevel 共同控制 contours 層級。
? ? 參數 offset 表示繪制時平移值。
三 Contour Operation
? ? 1 cv::approxPolyDP() 對? contour 進行多邊形近似,具體定義如下:
? ? void cv::approxPolyDP(cv::InputArray curve, cv::OutputArray approxCurve, double epsilon, bool closed);
? ? 參數 curve 可以是 vector<cv::Point>, vector<cv::Point2f>? 或者 arrays of size N*2, arrays of size N*1 with 2 channels。
? ? 參數 epsilon 表示近似精度,一般通過 contour 長度的百分比計算得出。
? ? 參數 closed 表示 contour 是否閉合。
? ??cv::approxPolyDP() 函數使用 Douglas-Peucker approximation,基本思路如下:
? ? 1)在 contour 上尋找距離最大的兩個點,將 contour 一分為二, 并連接兩點構成線段S;
? ? 2)? 在兩個半邊緣上分別尋找到到線段S上的最遠點,將半邊緣一分為二,連接以上四個點形成四邊形;
?? 3)繼續尋找到四邊形各條邊上的最遠點,構成多邊形,知道最遠點到對應邊上距離小于epsilon停止。
?? 2 double cv::arcLength(cv::InputArrray points, bool closed) 求 contour 長度。
?? 3 double cv::contourArea(cv::InputArray points, bool oriented = false) 使用格林公式求 contour 所圍成的面積,oriented = true 時返回帶符號面積值,該函數對復雜區域(如自交區域)將返回錯誤結果。
?? 4 cv::Rect cv::boundingRect(cv::InputArray points) 求 contour 所圍成的矩形(無旋轉矩形)。
?? 5 cv::RotateRect cv::minAreaRect(cv::InputArray points) 求 contour 所圍成的最小矩形(旋轉矩形)。
?? 6 void cv::minEnclosingCircle(cv::InputArray points, cv::Point2f& center, float& radius)?求 contour 所圍成的最小圓形。
?? 7 cv::RotateRect cv::fitEllipse(cv::InputArray points) 使用 contour 擬合橢圓(使用最小化代價函數)。
?? 8 cv::fitLine() 擬合直線,具體定義如下:
? ? void cv::fitLine(cv::InputArray points, cv::OutputArray line, int distType, double param, double reps, double aeps);
? ? 參數 points 接受二維或者三維點,擬合成二維平面直線或者三維空間直線。
? ? 參數 line 為 vec4f 或者 vec6f, 前兩個(或三個)元素給出直線方向,后兩個(或三個)給出直線上一個點。
? ? 參數? distType 表示距離度量范數,一般使用cv::DIST_L2, cv::DIST_L1。
? ? 參數 reps aeps 表示直線方向與直線點的計算精度,一般賦值為 .01 即可。
?? 9 void cv::convexHull(cv::InputArray points, cv::OutputArray hull, bool clockwise = false, bool returnPoints = true) 計算 contour 凸包,凸包點集計算比較簡單。
?? 10 bool cv::isContourConvex(cv::InputArray contour) 判斷? contour? 凸性。
?? 11 double cv::pointPolygonTest(cv::InputArray contour, cv::Point2f pt, bool measureDist) 判斷點是否在 contour 中,如果 measureDist? = true, 返回點到最近邊緣距離。