CV

findContours 轮廓检测

2017-07-17  本文已影响28人  谢小帅

1. findContours 找轮廓

void findContours( InputOutputArray image, OutputArrayOfArrays contours,
                              OutputArray hierarchy, int mode,
                              int method, Point offset = Point());
hierarchy[i][0]: the next contour at the same hierarchical level
hierarchy[i][1]: the previous contour at the same hierarchical level
hierarchy[i][2]: the first child contour
hierarchy[i][3]: the parent contour
  • CV_CHAIN_APPROX_NONE stores absolutely all the contour points. That is, any 2 subsequent points (x1,y1) and (x2,y2) of the contour will be either horizontal, vertical or diagonal neighbors.
    that is, max(abs(x1-x2),abs(y2-y1))==1.
    存储轮廓内所有相邻(水平、垂直、对角)的点
  • This is useful if the contours are extracted from the image ROI and then they should be analyzed in the whole image context. 从 ROI 转移到 全局图片

2. 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() );

3. 实例代码

核心部分是回调函数

void on_trackbar(int, void *)
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <iomanip>

using namespace cv;
using namespace std;

Mat img;
int threshval = 160; // 轨迹条滑块对应的值,给初值160,命名这里用threshold会ambiguous

// 扫描灰度图像矩阵
void scanImageMatrixG(Mat &img) {
    for (int i = 0; i < 10; ++i) {
        for (int j = 0; j < 10; ++j) {
            cout << setw(3) << (int) img.at<uchar>(i, j) << ", ";
        }
        cout << endl;
    }
    cout << endl << endl;
}

void on_trackbar(int, void *) {

    // 通过 threshval 二值化图像
    // threshval = 160 > 128, 选择 img > threshval
    // img 像素值 >160, 取 255,<=160,取 0
    Mat threshImage = threshval < 128 ? (img < threshval) : (img > threshval);

    cout << getTrackbarPos("threshold", "trackbar") << endl;
    scanImageMatrixG(threshImage);

    // 输出的点的位置
    vector<vector<Point>> contours; // 二维的点 { {[1,1], [1,2]}, ... }

    // 输出图像有4个通道
    vector<Vec4i> hierarchy; // vector<Vec<int, 4>> { { [1,2,3,4], [1,1,1,1], ... }, ... }

    // 寻找轮廓
    // CV_RETR_CCOMP: 取回轮廓采用两层结构
    // CV_CHAIN_APPROX_SIMPLE: 轮廓估计采用压缩轮廓
    findContours(threshImage, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);

    // 必须用zeros初始化dst
    // 回调函数每次调用目标矩阵dst都必须是个空矩阵
    // 不然会在上一个矩阵的基础上叠加值
    Mat dst = Mat::zeros(img.size(), CV_8UC3); // 类方法

    // 轮廓上色
    if (!contours.empty() && !hierarchy.empty()) {
        // 遍历所有顶层轮廓,顶层体现在 hierarchy[idx][0],同层
        // 随机生成颜色值绘制给各连接组成部分
        int idx = 0;
        for (; idx >= 0; idx = hierarchy[idx][0]) { // hierarchy[idx][0]: the next contour at the same hierarchical level
            Scalar color((rand() & 255), (rand() & 255), (rand() & 255)); // 随机色
            drawContours(dst, contours, idx, color, CV_FILLED, 8, hierarchy); // 绘制填充轮廓
        }
    }
    
    // 显示轮廓检测后的图像
    imshow("trackbar", dst);
}


int main() {

    // 读入灰度图
    img = imread("../pictures/pig.jpg", 0);

    // 显示原图
    namedWindow("img");
    imshow("img", img);

    // 原图灰度矩阵
    scanImageMatrixG(img);

    // 创建处理窗口
    namedWindow("trackbar");

    // 创建轨迹条
    createTrackbar("threshold", "trackbar", &threshval, 255, on_trackbar);

    // 轨迹条回调函数
    on_trackbar(threshval, 0); // threshval: 轨迹条位置,是全局变量,userdata = 0

    waitKey(0);
    return 0;
}
(1)threshold = 160

原图灰度矩阵 & 二值矩阵 threshImage

 64,  65,  66,  68,  68,  68,  68,  67,  58,  63, 
 63,  64,  66,  67,  68,  68,  67,  67,  58,  62, 
 65,  67,  68,  70,  71,  71,  70,  70,  65,  68, 
 75,  76,  78,  80,  81,  81,  81,  80,  82,  85, 
 94,  96,  97,  99, 101, 101, 101, 100, 103, 105, 
120, 121, 123, 125, 127, 127, 127, 127, 124, 126, 
145, 146, 148, 151, 152, 153, 153, 153, 145, 147, 
160, 162, 164, 166, 168, 168, 168, 168, 162, 163, 
170, 171, 173, 175, 176, 176, 176, 175, 175, 175, 
174, 176, 178, 180, 182, 183, 183, 183, 181, 181, 


160
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 
(2)threshold = 45

二值矩阵 threshImage

45
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,

因为判定方法为

Mat threshImage = threshval < 128 ? (img < threshval) : (img > threshval);

45 < 128, 选择 img < threshval,而像素值全部 >45,所以全为 0

(3)threshold = 200

二值矩阵 threshImage

200
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 

因为判定方法为

Mat threshImage = threshval < 128 ? (img < threshval) : (img > threshval);

200 > 128, 选择 img > threshval,而像素值全部 <200,所以全为 0

4. 总结

轮廓检测的步骤:

  1. imread("pic", 0) 读入灰度图
img = imread("../pictures/pig.jpg", 0);
  1. threshold 将灰度图分为二值图 (阈值影响着轮廓检测精确度)
Mat threshImage = threshval < 128 ? (img < threshval) : (img > threshval);
  1. findContours 寻找图像轮廓
// 寻找轮廓
// CV_RETR_CCOMP: 取回轮廓采用两层结构
// CV_CHAIN_APPROX_SIMPLE: 轮廓估计采用压缩轮廓
findContours(threshImage, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
  1. drawContours 画出找到的轮廓
// 轮廓上色
if (!contours.empty() && !hierarchy.empty()) {
    // 遍历所有顶层轮廓,顶层体现在 hierarchy[idx][0],同层
    // 随机生成颜色值绘制给各连接组成部分
    int idx = 0;
    for (; idx >= 0; idx = hierarchy[idx][0]) { // hierarchy[idx][0]: the next contour at the same hierarchical level
        Scalar color((rand() & 255), (rand() & 255), (rand() & 255)); // 随机色
        drawContours(dst, contours, idx, color, CV_FILLED, 8, hierarchy); // 绘制填充轮廓
    }
}

轮廓上色用随机色的好处:一种颜色就代表了一个轮廓

如果用单一的颜色,比如白色

Scalar color(255, 255, 255); // 白色

那么所有的轮廓就不能区分开了

上一篇 下一篇

猜你喜欢

热点阅读