OpenCV 为轮廓创建边界框和圆(62)

news/2024/9/25 14:17:28/

 返回: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


http://www.ppmy.cn/news/1453911.html

相关文章

【大模型学习】大模型相关概念

知识库 Embeding 嵌入&#xff0c;又称向量化、矢量化。 Prompt engineer 提示词工程 提示工程技巧 RAG 检索增强生成&#xff0c;提高文本的准确性和丰富性。 Fine tuning 微调&#xff0c;优化已有人工智能模型以适应特定任务的技术。 AI agent AI代理人&…

String str = new String(“Hello, World!“);

在Java中&#xff0c;当你执行 String str new String("Hello, World!"); 这行代码时&#xff0c;实际上发生了几件事情&#xff1a; 字符串常量池中的字符串&#xff1a;首先&#xff0c;Java会检查字符串字面量 "Hello, World!" 是否已经在字符串常量池…

HCIP-Datacom-ARST必选题库_路由协议【道题】

单选题 442/1327. 策略路由(policy-based-route)不支持根据下列哪种策略来指定数据包转发的路径? 源地址 8 目的地址 源MAC 报文长度 单选题 10/1327、下面是关于路由选择工具的描述,其中表述错误的是 route- policy只能匹配路由和数据包,并不能用来修改路由属性或者数…

与绿丰源创投世纪:共同引领环保金融的战略合作伙伴

在环保事业的蓬勃发展和可持续金融的兴起之际&#xff0c;沛然绿色资本&#xff08;和绿丰源创投世纪携手共建战略合作伙伴关系&#xff0c;旨在共同引领环保金融领域的发展&#xff0c;并为全球环保事业注入新的活力和动力。此次合作将充分发挥双方在金融、技术和环保领域的优…

【论文阅读】Learning Texture Transformer Network for Image Super-Resolution

Learning Texture Transformer Network for Image Super-Resolution 论文地址Abstract1. 简介2.相关工作2.1单图像超分辨率2.2 Reference-based Image Super-Resolution 3. 方法3.1. Texture TransformerLearnable Texture Extractor 可学习的纹理提取器。Relevance Embedding.…

01_SpringBoot简单搭建入门程序

目录 1、先创建一个java项目2、导入依赖3、将Java项目修改为SpringBoot项目4、编写一个测试的Controller5、测试(创建一个*.http的文件)方式1&#xff1a;方式2&#xff1a;可以直接在浏览器访问该地址方式3&#xff1a;使用postman也可以 1、先创建一个java项目 我的项目结构…

6-动态路由

前文提到用户可以通过url访问到不属于自己权限的页面&#xff0c;这需要通过动态路由来解决 1.将后端返回的菜单数据存储到Cookie之后&#xff0c;调用状态管理中添加路由方法 //调用store中的setMenu函数this.$store.commit(setMenu,data.data.data)// 动态路由配置this.$sto…

c#word文档:1.创建空白Word文档及保存/2.添加页内容...

---创建空白Word文档 --- &#xff08;1&#xff09;创建一个名为OfficeOperator的类库项目。引用操作Word的.NET类库 &#xff08;2&#xff09;定义用于操作Word的类WordOperator1。添加引用Microsoft.Office.Interop.Word命名空间。 &#xff08;3&#xff09;为WordOper…