计算区域的形状描述子 - sumpig/OpenCV GitHub Wiki


本节将介绍几种OpenCV 的形状描述子,用于描述连续区域的形状。

边界框

它的定义是:能完整包含该形状的最小垂直矩形。

// 测试边界框
cv::Rect r0 = cv::boundingRect(contours[0]);

// 画矩形
cv::rectangle(result, r0, 0, 2)

最小覆盖圆

最小覆盖圆通常在只需要区域尺寸和位置的近似值时使用。

// 测试覆盖圆
float radius;
cv::Point2f center;
cv::minEnclosingCircle(contours[1], center, radius);

// 画圆形
cv::circle(result, center, static_cast<int>(radius), cv::Scalar(0),2);

多边形逼近

如果要更紧凑地表示区域的形状,可采用多边形逼近。

cv::approxPolyDP 函数的第四个参数,表示形状与对应的简化多边形之间能接受的最大距离。

返回的结果是 cv::Point 类型的向量,表示多边形的顶点个数。

// 测试多边形逼近
std::vector<cv::Point> poly;
cv::approxPolyDP(contours[2], poly, 5, true);

// 画多边形
cv::polylines(result, poly, true, 0, 2);

多边形绘制函数cv::polylines 与其他画图函数很相似。第三个布尔型参数表示该轮廓是否闭合。

凸包

形状的凸包(或凸包络)是包含该形状的最小凸多边形。可以把它看作一条绕在区域周围的橡皮筋。

在形状轮廓中凹进去的位置,凸包轮廓会与原始轮廓发生偏离。通常可用凸包缺陷来表示这些位置。

// 测试凸包
std::vector<cv::Point> hull;
cv::convexHull(contours[3], hull);

// 画多边形
cv::polylines(result, hull, true, 0, 2);

OpenCV 中有一个专门用于识别凸包缺陷的函数 cv::convexityDefects:

std::vector<cv::Vec4i> defects;
cv::convexityDefects(contour, hull, defects);

参数 contour 和 hull 分别表示原始轮廓和凸包轮廓。函数输出的是一个向量,它的每个元素由四个整数组成:前两个整数是顶点在轮廓中的索引,用来界定该缺陷;第三个整数表示凹陷内部最远的点;最后的整数表示最远点与凸包之间的距离。

轮廓矩(在所有区域内部画出重心)

// 测试轮廓矩
// 迭代遍历所有轮廓
itc= contours.begin();
while (itc!=contours.end()) {

    // 计算所有轮廓矩
    cv::Moments mom= cv::moments(cv::Mat(*itc++));

    // 画重心
    cv::circle(result,
               // 将重心位置转换成整数
               cv::Point(mom.m10/mom.m00,mom.m01/mom.m00),
               2, cv::Scalar(0), 2); // 画黑点
}

结构化属性

函数 cv::minAreaRect 计算最小覆盖自由矩形
函数 cv::contourArea 估算轮廓的面积(内部的像素数量)
函数 cv::pointPolygonTest 判断一个点在轮廓的内部还是外部
函数 cv::matchShapes 度量两个轮廓之间的相似度

四边形检测

// 创建二值图像
components = components==255;

// 打开图像(包含背景)
cv::morphologyEx(components, components,
                 cv::MORPH_OPEN, cv::Mat(),
                 cv::Point(-1,-1), 3);

// 翻转图像(背景必须是黑色的)
cv::Mat componentsInv = 255 - components;

// 得到连续区域的轮廓
cv::findContours(componentsInv,
                 contours,          // 轮廓的向量
                 cv::RETR_EXTERNAL, // 检索外部轮廓
                 cv::CHAIN_APPROX_NONE);

// 白色图像
cv::Mat quadri(components.size(), CV_8U, 255);

// 针对全部轮廓
std::vector<std::vector<cv::Point>>::iterator it = contours.begin();
while (it != contours.end()) {
    poly.clear();
    // 用多边形逼近轮廓
    cv::approxPolyDP(*it, poly, 10, true);
    // 是否为四边形?
    if (poly.size()==4)
        cv::polylines(quadri, poly, true, 0, 2);
    ++it;
}

要检测矩形,只需测量相邻边的夹角,并且排除夹角与90 度相差很大的四边形。

⚠️ **GitHub.com Fallback** ⚠️