opencv(24)---轮廓特征属性及应用之最小外接圆

本文介绍使用OpenCV进行轮廓分析的方法,包括最小外接圆、椭圆拟合、逼近多边形曲线、计算轮廓面积和长度等操作,并展示了如何通过掩码提取不规则轮廓。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最小外接圆

函数原型—minEnclosingCircle()

void minEnclosingCircle( InputArray points,
                         CV_OUT Point2f& center, CV_OUT float& radius );
  • points: 输入的二维点集, 可以填Mat类型或std::vector
  • center: Point2f&类型的center, 圆的输出圆心
  • radius: float&类型, 表示圆的输出半径

应用实例

这里写图片描述

代码

Mat srcImg = imread("D:\\1\\10.png");
imshow("src", srcImg);
Mat dstImg = srcImg.clone();
GaussianBlur(srcImg, srcImg, Size(3, 3), 0, 0);
cvtColor(srcImg, srcImg, CV_BGR2GRAY);
Canny(srcImg, srcImg, 100, 200);
imshow("Canny", srcImg);

vector<vector<Point>> contours;
vector<Vec4i> hierarcy;

findContours(srcImg, contours, hierarcy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
cout<<"num="<<contours.size()<<endl;

Point2f center;  //定义圆中心坐标
float radius;  //定义圆半径
for(int i=0; i<contours.size(); i++)  //依次遍历每个轮廓
{
    minEnclosingCircle(Mat(contours[i]), center, radius);
    drawContours(dstImg, contours, i, Scalar(0, 0, 255), 2, 8);
    circle(dstImg, center, radius, Scalar(0, 255, 0), 2, 8);  //绘制第i个轮廓的最小外接圆
}
imshow("dst", dstImg);

 waitKey(0);

运行结果

这里写图片描述

这里写图片描述

这里写图片描述

椭圆拟合

函数原型—fitEllipse()

RotatedRect fitEllipse( InputArray points );
  • points: 输入的二维点集, 可以填Mat类型或std::vector
  • 返回值: RotatedRect类旋转矩形对象

应用实例

这里写图片描述

代码

Mat srcImg = imread("D:\\1\\10.png");
imshow("src", srcImg);
Mat dstImg = srcImg.clone();
GaussianBlur(srcImg, srcImg, Size(3, 3), 0, 0);
cvtColor(srcImg, srcImg, CV_BGR2GRAY);
Canny(srcImg, srcImg, 100, 200);
imshow("Canny", srcImg);

vector<vector<Point>> contours;
vector<Vec4i> hierarcy;

findContours(srcImg, contours, hierarcy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
cout<<"num="<<contours.size()<<endl;
vector<RotatedRect> box(contours.size());
Point2f rect[4];
for(int i=0; i<contours.size(); i++)
{
    box[i] = fitEllipse(Mat(contours[i]));
    //ellipse(dstImg, box[i].center, Size(box[i].size.width/2, box[i].size.height/2), box[i].angle, 0, 360, Scalar(0, 255, 0), 2, 8);
    ellipse(dstImg, box[i], Scalar(0, 255, 0), 2, 8);
}

imshow("dst", dstImg);

waitKey(0);

运行结果

这里写图片描述

这里写图片描述

这里写图片描述

逼近多边形曲线

函数原型—approxPolyDP()

void approxPolyDP( InputArray curve,
                   OutputArray approxCurve,
                   double epsilon, bool closed );
  • curve: 输入的二维点集, 可以填Mat类型或std::vector
  • approxCurve: 多边形逼近的结果, 其类型和输入二维点集类型一致
  • epsilon: 逼近的精度, 为原始曲线和近似曲线间的最大值
  • closed: 如果其为真, 则近似的曲线为封闭曲线, 否则近似的曲线不封闭

应用实例

这里写图片描述

1. 当图像有缺口时,轮廓逼近后逼近为完整的图像
2. 当查找外接矩形时,先进行轮廓逼近,再查找外接矩形

代码

Mat srcImg = imread("22.jpg");
imshow("src", srcImg);
Mat dstImg = srcImg.clone();
Mat dstImg2(srcImg.size(), CV_8UC3, Scalar::all(0));

GaussianBlur(srcImg, srcImg, Size(3, 3), 0, 0);
cvtColor(srcImg, srcImg, CV_BGR2GRAY);
//Canny(srcImg, srcImg, 100, 200);
threshold(srcImg, srcImg, 200, 255, CV_THRESH_BINARY_INV);
imshow("threshold", srcImg);

vector<vector<Point>> contours;
vector<Vec4i> hierarcy;
findContours(srcImg, contours, hierarcy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
cout<<"num="<<contours.size()<<endl;
vector<vector<Point>> contours_poly(contours.size());

for(int i=0; i<contours.size(); i++)
{
    approxPolyDP(Mat(contours[i]), contours_poly[i], 15, true);
    drawContours(dstImg, contours, i, Scalar(0, 255, 0), 2, 8);
    drawContours(dstImg2, contours_poly, i, Scalar(0, 255, 255), 2, 8);  //绘制多边形逼近
}

imshow("dst", dstImg);
imshow("approx", dstImg2);

waitKey(0);

运行结果

srcImg

这里写图片描述

thresholdImg
这里写图片描述

轮廓图
这里写图片描述

多边形趋近
这里写图片描述

计算轮廓面积

函数原型

double contourArea( InputArray contour, bool oriented = false );
  • contour: 输入的二维点集或轮廓, 可以填Mat类型或std::vector
  • oriented: 默认值false, 表示返回面积为绝对值, 负责带符号
    返回值: double类型返回轮廓面积

代码


Mat srcImg = imread("33.jpg");
imshow("src", srcImg);
Mat dstImg = srcImg.clone();
Mat dstImg2(srcImg.size(), CV_8UC3, Scalar::all(0));

GaussianBlur(srcImg, srcImg, Size(3, 3), 0, 0);
cvtColor(srcImg, srcImg, CV_BGR2GRAY);
//Canny(srcImg, srcImg, 100, 200);
threshold(srcImg, srcImg, 200, 255, CV_THRESH_BINARY);
imshow("threshold", srcImg);

vector<vector<Point>> contours;
vector<Vec4i> hierarcy;
findContours(srcImg, contours, hierarcy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
cout<<"num="<<contours.size()<<endl;

for(int i=0; i<contours.size(); i++)
{
    double area = contourArea(contours[i]);
    cout<<"area--"<<i<<"---"<<area<<endl;

    if(area>10000)  //面积大约1W
    drawContours(dstImg, contours, i, Scalar(0, 0, 255), 2, 8);
}

imshow("dst", dstImg);
waitKey(0);  

计算轮廓长度(周长或者曲线长度)

函数原型—arcLength()

double arcLength( InputArray curve, bool closed );
  • curve: 输入的二维点集, 可以填Mat类型或std::vector
  • colsed: 用于指示曲线是否封闭的标识符, 默认值true, 表示曲线封闭
    返回值: double类型返回轮廓长度

注:contourArea()&& arcLength()可用于轮廓删选

代码

Mat srcImg = imread("33.jpg");
imshow("src", srcImg);
Mat dstImg = srcImg.clone();
Mat dstImg2(srcImg.size(), CV_8UC3, Scalar::all(0));

GaussianBlur(srcImg, srcImg, Size(3, 3), 0, 0);
cvtColor(srcImg, srcImg, CV_BGR2GRAY);
//Canny(srcImg, srcImg, 100, 200);
threshold(srcImg, srcImg, 200, 255, CV_THRESH_BINARY);
imshow("threshold", srcImg);

vector<vector<Point>> contours;
vector<Vec4i> hierarcy;
findContours(srcImg, contours, hierarcy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
cout<<"num="<<contours.size()<<endl;

for(int i=0; i<contours.size(); i++)
{

   double length = arcLength(contours[i], true);
   cout<<"length--"<<i<<"---"<<length<<endl;
   if(length>300)  //轮廓曲线长度大于300
       drawContours(dstImg, contours, i, Scalar(0, 0, 255), 2, 8);
}

imshow("dst", dstImg);
waitKey(0);  

利用mask提取不规则轮廓

应用实例

这里写图片描述

代码

Mat srcImg = imread("D:\\1\\220.jpg");
imshow("src", srcImg);
Mat dstImg = srcImg.clone();  //原图备份
Mat tempImg = srcImg.clone();  //原图备份
Mat tempImg2(srcImg.rows, srcImg.cols, CV_8UC3, Scalar::all(0));  //定义全黑的和原图一样大小的图像
Mat draw(srcImg.rows, srcImg.cols, CV_8UC3, Scalar::all(0)); //定义全黑的和原图一样大小的图像
Mat tempImg3(srcImg.rows, srcImg.cols, CV_8UC3, Scalar::all(0)); //定义全黑的和原图一样大小的图像

GaussianBlur(srcImg, srcImg, Size(3, 3), 0, 0);
cvtColor(srcImg, srcImg, CV_BGR2GRAY);
threshold(srcImg, srcImg, 100, 255, CV_THRESH_BINARY); //二值化
imshow("threshold", srcImg);

vector<vector<Point>> contours;
vector<Vec4i> hierarcy;

findContours(srcImg, contours, hierarcy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
cout<<"num="<<contours.size()<<endl;
while(1)
{
    for(int i=0; i<contours.size(); i++)
    {
        tempImg2.copyTo(draw);  //每次进入将draw清空为全黑
        tempImg2 .copyTo(tempImg3); //每次进入将tempImg3清空为全黑
        //drawContours(dstImg, contours, i, Scalar(0, 255, 0), 5, 8);
        drawContours(draw, contours, i, Scalar(255, 255, 255), -1, 8);
        Mat mask;  //定义掩码
        cvtColor(draw, mask, CV_BGR2GRAY);
        tempImg.copyTo(tempImg3, mask);  //将tempImg 复制到tempImg3(只有mask部分被复制)
        imshow("draw", draw);
        imshow("result", tempImg3);
        char key = waitKey();
        if(key==27)  //按下Esc键跳出for循环
            break;
    }
    break;
}

运行结果

srcImg

这里写图片描述

threshold

这里写图片描述

draw

这里写图片描述

result

这里写图片描述

知识点讲解

掩码
参考以前的opencv学习之掩码
dstImg,tempImg都是对原图的备份;tempImg2,draw,tempImg3都是全黑的图像

Mat dstImg = srcImg.clone();  //原图备份
Mat tempImg = srcImg.clone();  //原图备份
Mat tempImg2(srcImg.rows, srcImg.cols, CV_8UC3, Scalar::all(0));  //定义全黑的和原图一样大小的图像
Mat draw(srcImg.rows, srcImg.cols, CV_8UC3, Scalar::all(0)); //定义全黑的和原图一样大小的图像
Mat tempImg3(srcImg.rows, srcImg.cols, CV_8UC3, Scalar::all(0)); //定义全黑的和原图一样大小的图像

每次绘制都将draw和tempImg3清空为全黑。将轮廓(内部填充为白色)绘制到draw图像,将draw图像转为灰度的掩码图像mask,然后进行掩码的复制操作

tempImg2.copyTo(draw);  //每次进入将draw清空为全黑
tempImg2 .copyTo(tempImg3); //每次进入将tempImg3清空为全黑
//drawContours(dstImg, contours, i, Scalar(0, 255, 0), 5, 8);
drawContours(draw, contours, i, Scalar(255, 255, 255), -1, 8);
Mat mask;  //定义掩码
cvtColor(draw, mask, CV_BGR2GRAY);
tempImg.copyTo(tempImg3, mask);  //将tempImg 复制到tempImg3(只有mask部分被复制)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值