【opencv 450 Image Processing】Creating Bounding boxes and circles for contours为轮廓创建边界框和圆

Goal

在本教程中,您将学习如何:

使用 OpenCV 函数 cv::boundingRect

使用 OpenCV 函数 cv::minEnclosureCircle

Theory

Code

本教程代码如下所示。 你也可以从这里下载 https://github.com/opencv/opencv/tree/4.x/samples/cpp/tutorial_code/ShapeDescriptors/generalContours_demo1.cpp

/**
 * @function generalContours_demo1.cpp
 * @brief 在图像中查找轮廓的演示代码
 * @author OpenCV team
 */

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>

using namespace cv;
using namespace std;

Mat src_gray;
int thresh = 100;
RNG rng(12345);

/// Function header
void thresh_callback(int, void* );

/**
 * @function main
 */
int main( int argc, char** argv )
{
    //! [setup]
    /// 加载源图像
    CommandLineParser parser( argc, argv, "{@input | stuff.jpg | input image}" );
    Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
    if( src.empty() )
    {
        cout << "Could not open or find the image!\n" << endl;
        cout << "usage: " << argv[0] << " <Input image>" << endl;
        return -1;
    }

    /// 将图像转换为灰度图并模糊它
    cvtColor( src, src_gray, COLOR_BGR2GRAY );
    blur( src_gray, src_gray, Size(3,3) );//均值滤波
    //! [setup]

    //! [createWindow]
    /// 创建窗口
    const char* source_window = "Source";
    namedWindow( source_window );
    imshow( source_window, src );//显示源图像
    //! [createWindow]

    //! [trackbar]
    const int max_thresh = 255;
    createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback );//创建滑动条
    thresh_callback( 0, 0 );//回调
    //! [trackbar]

    waitKey();
    return 0;
}

/**
 * @function thresh_callback
 */
void thresh_callback(int, void* )
{
    //! [Canny]
    /// 使用 Canny 检测边缘 Detect edges using Canny
    Mat canny_output;
    Canny( src_gray, canny_output, thresh, thresh*2 );
    //! [Canny]

    //! [findContours]
    /// 寻找轮廓 Find contours
    vector<vector<Point> > contours;
    findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );
    //! [findContours]

    //! [allthework]
    /// 近似轮廓到多边形 + 获得边界矩形和圆形 Approximate contours to polygons + get bounding rects and circles
    vector<vector<Point> > contours_poly( contours.size() );//近似轮廓多边形
    vector<Rect> boundRect( contours.size() );//边界矩形
    vector<Point2f>centers( contours.size() );//边界圆圆心
    vector<float>radius( contours.size() );//边界圆半径

    for( size_t i = 0; i < contours.size(); i++ )//遍历轮廓点
    {
        approxPolyDP( contours[i], contours_poly[i], 3, true );//对图像轮廓点进行多边形拟合
        boundRect[i] = boundingRect( contours_poly[i] );//boundingRect来对指定的点集进行包含,使得形成一个最合适的正向矩形框把当前指定的点集都框住.计算轮廓的垂直边界最小矩形,矩形是与图像上下边界平行的
        minEnclosingCircle( contours_poly[i], centers[i], radius[i] );//寻找包裹轮廓的最小圆
    }
    //! [allthework]

    //! [zeroMat]
    Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );//黑色背景图
    //! [zeroMat]

    //! [forContour]
    /// 绘制多边形轮廓+边界矩形+圆形 Draw polygonal contour + bonding rects + circles
    for( size_t i = 0; i< contours.size(); i++ )
    {
        Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );//随机颜色
        drawContours( drawing, contours_poly, (int)i, color );//轮廓多边形
        rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2 );//矩形
        circle( drawing, centers[i], (int)radius[i], color, 2 );//圆
    }
    //! [forContour]

    //! [showDrawings]
    /// 显示结果Show in a window
    imshow( "Contours", drawing );
    //! [showDrawings]
}

Explanation

main 函数比较简单,从注释中我们做了如下:

  1. 打开图像,将其转换为灰度并对其进行模糊处理以消除噪点。

    CommandLineParser parser( argc, argv, "{@input | stuff.jpg | input image}" );

    Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) );

    if( src.empty() )

    {

        cout << "Could not open or find the image!\n" << endl;

        cout << "usage: " << argv[0] << " <Input image>" << endl;

        return -1;

    }

    cvtColor( src, src_gray, COLOR_BGR2GRAY );

    blur( src_gray, src_gray, Size(3,3) );

2. 创建一个带有标题“Source”的窗口并在其中显示源文件。

    const char* source_window = "Source";

    namedWindow( source_window );

    imshow( source_window, src );

3.在 source_window 上创建一个跟踪栏并为其分配一个回调函数。 一般来说,回调函数用于对某种信号做出反应,在我们的例子中是滑动条的状态变化。 要同时显示“轮廓”窗口和“源”窗口,需要显式地一次性调用 thresh_callback

    const int max_thresh = 255;

    createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback );

    thresh_callback( 0, 0 );

回调函数完成了所有有趣的工作。

4. 使用 cv::Canny 检测图像中的边缘。

    Mat canny_output;

    Canny( src_gray, canny_output, thresh, thresh*2 );

5. 查找轮廓并将它们保存到向量轮廓和层次结构中。

    vector<vector<Point> > contours;

    findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );

6. 对于每个找到的轮廓,我们现在将近似值应用于精度为 +-3 的多边形,并声明曲线必须是闭合的。 之后,我们为每个多边形找到一个边界矩形并将其保存到 boundRect。 最后,我们为每个多边形找到一个最小包围圆,并将其保存到中心和半径向量。

    vector<vector<Point> > contours_poly( contours.size() );

    vector<Rect> boundRect( contours.size() );

    vector<Point2f>centers( contours.size() );

    vector<float>radius( contours.size() );

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

    {

        approxPolyDP( contours[i], contours_poly[i], 3, true );

        boundRect[i] = boundingRect( contours_poly[i] );

        minEnclosingCircle( contours_poly[i], centers[i], radius[i] );

    }

我们找到了我们需要的一切,我们所要做的就是画画。

7. 创建新的无符号 8 位字符 Mat,用零填充。 它将包含我们要制作的所有图纸(矩形和圆圈)。

   Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );

8. 对于每个轮廓:选择一种随机颜色,用它绘制轮廓、边界矩形和最小的封闭圆。

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

    {

        Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );

        drawContours( drawing, contours_poly, (int)i, color );

        rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2 );

        circle( drawing, centers[i], (int)radius[i], color, 2 );

    }

9. 显示结果:创建一个新窗口“轮廓”并在其上显示我们添加到图纸中的所有内容。

    imshow( "Contours", drawing );

Result

 

参考:

cv.approxPolyDP多边拟合函数

 cv.approxPolyDP() 的参数1是源图像的某个轮廓;参数2(epsilon)是一个距离值,表示多边形的轮廓接近实际轮廓的程度,值越小,越精确;参数3表示是否闭合。

C++: void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed)

参数详解;

InputArray curve:一般是由图像的轮廓点组成的点集

OutputArray approxCurve:表示输出的多边形点集,当前点集是能最小包容指定点集的。draw出来即是一个多边形;

double epsilon:主要表示输出的精度,也即是原始曲线与近似曲线之间的最大距离。

bool closed:表示输出的多边形是否封闭

openCV之approxPolyDP()多边拟合函数_zhangweiwei1516的博客-CSDN博客_approxpolydp 获取四个角点

寻找包裹轮廓的最小圆:minEnclosingCircle 函数

返回圆应满足:① 轮廓上的点均在圆形空间内。② 没有面积更小的满足条件的圆。

void minEnclosingCircle(InputArray points, Point2f& center, float& radius);

points,输入的二维点集,可以是 vector 或 Mat 类型。

center,圆的输出圆心。

radius,圆的输出半径。

opencv —— minEnclosingCircle、fitEllipse 寻找包裹轮廓的最小圆、点集拟合椭圆 - 老干妈就泡面 - 博客园 (cnblogs.com)

OpenCV—Python 轮廓检测 绘出矩形框(findContours\ boundingRect\rectangle)_javastart的博客-CSDN博客_findcontours 矩形

Opencv视觉学习--Rect、RotatedRect类与boundingRect( )、minAreaRect( )、fitEllipse( )函数_佳佳鸽的博客-CSDN博客

minAreaRect( )函数返回参数如图:

角点顺序是固定以最下面的点为P[0],顺时针依次递增编号的。将水平轴逆时针旋转,最先与水平轴平行的边作为宽width,另外一条边则为高height,并非长的就是height,短的就是width。而角度则直接取最先与水平轴平行的边与水平轴的夹角,取值范围[-90~0]。

fitEllipse( )函数返回参数如图:

角度参数angle 是椭圆中心center发出的平行于x轴的射线,顺时针旋转,与椭圆短轴的夹角,取值范围 [ 0 ~ 180 ]。椭圆的短轴为参数宽width,长轴为参数长height。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值