返回:OpenCV系列文章目录(持续更新中......)
上一篇:OpenCV检测凸包(61)
下一篇 :OpenCV如何为等值线创建边界旋转框和椭圆(62)
目标
在本教程中,您将学习如何:
- 使用 OpenCV 函数 cv::boundingRect
- 使用 OpenCV 函数 cv::minEnclosingCircle
cv::boundingRect 和 cv::minEnclosingCircle 都是 OpenCV 库中常用的图像处理函数,主要用于轮廓绘制和弧形分析等操作。
cv::boundingRect 是一个用于计算轮廓边界的函数,它可以计算由轮廓点定义的矩形边框。该函数的主要思路是,在轮廓点坐标系中寻找最小的水平矩形(或垂直矩形),然后返回该矩形的左上角坐标和宽度、高度等信息。通过 boundingRect 函数,我们可以实现轮廓的外边框计算和绘制操作,使得轮廓分析更加精确和直观。
cv::minEnclosingCircle 则是一个用于计算轮廓最小外接圆的函数,它可以计算由轮廓点定义的最小圆形。该函数的主要思路是,在轮廓点中寻找最小的能包含所有点的圆形,然后返回该圆的圆心和半径信息。通过 minEnclosingCircle 函数,我们可以实现轮廓的最小外接圆计算和绘制,同时也可以实现对弧形、角度等特征的计算和分析。
cv::boundingRect 和 cv::minEnclosingCircle 通常会一起使用。它们可以用于轮廓分析、目标跟踪、形状检测等操作。通过 boundingRect 函数计算轮廓的边框,我们可以确定轮廓的矩形区域,从而实现轮廓的定位和绘制操作;通过 minEnclosingCircle 函数计算轮廓的最小圆形,我们可以确定轮廓的圆形区域,并计算出圆形的特征,从而实现更加精确的轮廓分析和检测。
C++代码
本教程代码如下所示。您也可以从这里下载
#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);void thresh_callback(int, void* );int main( int argc, char** argv )
{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) );const char* source_window = "Source";namedWindow( source_window );imshow( source_window, src );const int max_thresh = 255;createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback );thresh_callback( 0, 0 );waitKey();return 0;
}void thresh_callback(int, void* )
{Mat canny_output;Canny( src_gray, canny_output, thresh, thresh*2 );vector<vector<Point> > contours;findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );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] );}Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );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 );}imshow( "Contours", drawing );
}
解释
主要功能相当简单,从注释中我们执行以下操作:
- 打开图像,将其转换为灰度并模糊以消除噪点。
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) );
- 创建一个标题为“Source”的窗口,并在其中显示源文件。
const char* source_window = "Source";namedWindow( source_window );imshow( source_window, src );
- 在 “Source”的窗口上创建一个跟踪栏,并为其分配回调函数。通常,回调函数用于对某种信号做出反应,在我们的例子中,它是跟踪栏的状态变化。需要显式一次性调用thresh_callback才能同时显示“轮廓”窗口和“源”窗口。
const int max_thresh = 255;createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback );thresh_callback( 0, 0 );
回调函数完成了所有需要的工作。
- 使用 cv::Canny 检测图像中的边缘。
Mat canny_output;Canny( src_gray, canny_output, thresh, thresh*2 );
- 查找等值线并将它们保存到向量 contour和hierarchy中
vector<vector<Point> > contours;findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );
- 对于每个找到的等值线,我们现在以 +-3 的精度对多边形应用近似值,并指出曲线必须闭合。之后,我们为每个多边形找到一个边界矩形并将其保存到boundRect 。最后,我们为每个多边形找到一个最小封闭圆,并将其保存到center 和 radius向量。
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] );}
我们找到了我们需要的一切,我们所要做的就是画画。
- 创建新的无符号 8 位字符垫,填充零。它将包含我们将要制作的所有图纸(矩形和圆形)。
Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
- 对于每个等值线:选择一种随机颜色,用它绘制轮廓、边界矩形和最小封闭圆。
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 );}
- 显示结果:创建一个新窗口“轮廓”,并在其上显示我们添加到图纸中的所有内容。
imshow( "Contours", drawing );
结果
在这里:
参考文献:
1、《Creating Bounding boxes and circles for contours》------ Ana Huamán