OpenCV 笔记(14):图像的轮廓和轮廓的基础特征

Part11.  图像的轮廓

在该系列第三篇文章中,曾经简单地介绍过轮廓和轮廓发现。

11.1 轮廓的基本概念

图像的轮廓是指图像中具有相同颜色灰度值的连续点的曲线。轮廓和边缘是有联系的,边缘是轮廓的基础,轮廓是边缘的连续集合。

轮廓和边缘的区别是:

  • 轮廓是连续的,边缘可以是连续的,也可以是离散的。

  • 轮廓是完整的,边缘可以是完整的,也可以是不完整的。

  • 轮廓可以有各种形状,边缘通常是线性的。

21.2 轮廓发现和轮廓提取

轮廓发现是指在图像中找到所有可能的轮廓。

轮廓提取是指从图像中找到所有有效的轮廓和轮廓的具体信息。

轮廓发现是轮廓提取的前提,轮廓提取在轮廓发现的基础上进一步提取轮廓的形状和位置信息等等。

下面的代码,经过一系列操作找到二值图像的有效轮廓后,获取这些轮廓的最小外接矩形,最后用线在原图中框出这些外接矩形,从而在原图中找到比较明显的苹果。

#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"using namespace std;
using namespace cv;bool ascendSort(vector<Point> a,vector<Point> b)
{return contourArea(a) > contourArea(b);
}int main(int argc, char **argv) {Mat src = imread(".../apple.jpg");imshow("src", src);Mat hsv,edge;cvtColor(src, hsv, cv::COLOR_BGR2HSV); // BGR 转换到 HSV 色彩空间imshow("hsv", hsv);cv::Scalar lower_red(0, 43, 46);cv::Scalar upper_red(10, 255, 255); // 定义红色的 HSV 范围Mat mask;inRange(hsv, lower_red, upper_red, mask); // 通过 inRange 函数实现二值化imshow("mask", mask);Mat kernel = getStructuringElement(MORPH_RECT, Size(15, 15));morphologyEx(mask, mask, MORPH_CLOSE, kernel); // 形态学操作morphologyEx(mask, mask, MORPH_OPEN, kernel);  // 形态学操作imshow("morphology", mask);vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(mask, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);sort(contours.begin(), contours.end(), ascendSort);//ascending sortfor (size_t i = 0; i< contours.size(); i++) {double area = contourArea(contours[i]);if (area < 22000) {continue;}cout << "area = " << area << endl;RotatedRect rrt = minAreaRect(contours[i]);Point2f pt[4];rrt.points(pt);line(src, pt[0], pt[1], Scalar(255, 0, 0), 8, 8);line(src, pt[1], pt[2], Scalar(255, 0, 0), 8, 8);line(src, pt[2], pt[3], Scalar(255, 0, 0), 8, 8);line(src, pt[3], pt[0], Scalar(255, 0, 0), 8, 8);}imshow("result", src);waitKey(0);return 0;
}

展示原图

f49c839d6000026d9299f9faef93e14d.jpeg
苹果的原图.png

将原图转换成 HSV 类型,用于提取特定颜色。

d8a49c447babc6cd93c9fff92d225081.jpeg
hsv.png

通过 inRange 函数实现二值化。inRange 函数用于将图像中的像素值限制在指定的范围内,它会将满足条件的像素设置为 255,不满足条件的像素设置为 0,从而形成一个二值图像。

21e51690d8a1a282c78d96b145c3240d.jpeg
二值化.png

对二值图像进行一些形态学的操作,便于后续的轮廓分析。

24cb3aa7cc12e0d0d9c0020cc3bc013f.jpeg
形态学操作.png

通过 findContours() 函数进行轮廓发现。最后,筛选出有效的轮廓,并获取最小外接矩形,用线画出在原图上展示出来。

71fb4f6dfda2b11d630aa3e6bd1278e6.jpeg
找到有效的苹果.png

Part22.  轮廓特征的分类

图像的轮廓特征可以分为以下几类:

  • 基础特征:面积、周长、质心、凸包、最小外接矩形等。这些特征可以直接从轮廓序列中计算得到。

  • 矩特征:Hu 矩、中心矩、惯性矩等。这些特征可以用于描述轮廓的形状和大小。

  • 几何特征:最小闭合圆、拟合椭圆等。这些特征可以用于描述轮廓的几何形状。

Part33.  轮廓的基础特征

33.1 面积、周长、最小外接矩形

  • 轮廓面积 contourArea()

  • 轮廓周长 arcLength()

  • 轮廓外接矩形 boundingRect()

  • 轮廓最小外接矩形 minAreaRect()

下面的例子获取图中回形针的轮廓,以及轮廓的面积、周长、最小外接矩形等。

#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"using namespace std;
using namespace cv;bool ascendSort(vector<Point> a,vector<Point> b)
{return contourArea(a) > contourArea(b);
}int main(int argc, char **argv) {Mat src = imread(".../paperclip.jpg");imshow("src", src);Mat gray,thresh;cvtColor(src, gray, cv::COLOR_BGR2GRAY);imshow("gray", gray);threshold(gray,thresh,0,255,THRESH_BINARY_INV | THRESH_OTSU);imshow("thresh", thresh);vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(thresh, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);sort(contours.begin(), contours.end(), ascendSort);//ascending sortfor (size_t i = 0; i< contours.size(); i++) {double area = contourArea(contours[i]);double length = arcLength(contours[i],true);if (area < 1000) {continue;}cout << "area = " << area << ", length = " << length << endl;RotatedRect rrt = minAreaRect(contours[i]);// 获取最小外接矩形Point2f pt[4];rrt.points(pt);line(src, pt[0], pt[1], Scalar(255, 0, 0), 8, 8);line(src, pt[1], pt[2], Scalar(255, 0, 0), 8, 8);line(src, pt[2], pt[3], Scalar(255, 0, 0), 8, 8);line(src, pt[3], pt[0], Scalar(255, 0, 0), 8, 8);Point  center = rrt.center;circle(src, center, 2,Scalar(0, 0, 255), 8, 8); // 绘制最小外接矩形的中心点}imshow("result", src);waitKey(0);return 0;
}

执行结果:

area = 101573, length = 2461.71
area = 41757.5, length = 1256.08
area = 41348, length = 1152.56
area = 39717.5, length = 1616.13
area = 37503, length = 1230.47
area = 36742.5, length = 1037.21
area = 4142, length = 706.357
0e67e2cfdb005d416ed89e730469ec85.jpeg
回形针.png

外接矩形是指可以包围轮廓所有点的矩形,而最小外接矩形是指包含轮廓中所有点的最小矩形。

下面的例子,获取图中最大轮廓的外接矩形和最小外接矩形,分别用黄色和蓝色表示。

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"using namespace std;
using namespace cv;bool ascendSort(vector<Point> a,vector<Point> b)
{return contourArea(a) > contourArea(b);
}int main(int argc, char **argv) {Mat src = imread(".../fruit.jpg");imshow("src", src);Mat gray,thresh;cvtColor(src, gray, cv::COLOR_BGR2GRAY);threshold(gray,thresh,0,255,THRESH_BINARY | THRESH_OTSU);vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(thresh, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);sort(contours.begin(), contours.end(), ascendSort);//ascending sortRotatedRect rrt = minAreaRect(contours[0]);// 获取最大轮廓的最小外接矩形Point2f pt[4];rrt.points(pt);line(src, pt[0], pt[1], Scalar(255, 0, 0), 8, 8);line(src, pt[1], pt[2], Scalar(255, 0, 0), 8, 8);line(src, pt[2], pt[3], Scalar(255, 0, 0), 8, 8);line(src, pt[3], pt[0], Scalar(255, 0, 0), 8, 8);Rect rect = boundingRect(contours[0]);// 获取最大轮廓的外接矩形rectangle(src,rect,Scalar(0, 255, 255), 8, 8);// 绘制外接矩形imshow("result", src);waitKey(0);return 0;
}
4191c7d50c4e7c359ca5d5f2e1420314.jpeg
外接矩形和最小外接矩形.png

通过上述例子可以看到,最小外接矩形能够更精确地描述轮廓的形状和大小。

外接矩形和最小外接矩形有各自的使用场景,例如在对象检测中,可以使用外接矩形来粗略定位物体,而使用最小外接矩形来精确定位物体。

43.2 凸包

凸包(Convex Hull)是计算几何(图形学)中的概念。在一个实数向量空间 V 中,对于给定集合 X,所有包含 X 的凸集的交集 S 被称为 X 的 凸包。

在二维欧几里得空间中,凸包可想象为一条刚好包著所有点的橡皮圈。

  • 平面的一个子集 S 被称为是“凸”的,当且仅当对于任意两点 p,s ∈S,线段 ps 都完全属于S。

  • 一个点集 P 的凸包CH(P),就是包含 P 的最小凸集——即包含P的所有凸集的交。

凸包的性质:

  • 凸包是凸集。

  • 凸包的周长是最小的。

  • 凸包的面积是最小的。

  • 凸包的质心是所有点的质心的均值。

OpenCV 提供了 convexHull() 函数寻找轮廓的凸包以及 isContourConvex() 函数用于判断轮廓是否为凸轮廓。凸轮廓是指所有内角都小于或等于 180 度的轮廓。

#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"using namespace std;
using namespace cv;int main(int argc, char **argv) {Mat src = imread(".../hand.jpg");imshow("src", src);Mat gray,thresh;cvtColor(src, gray, cv::COLOR_BGR2GRAY);threshold(gray,thresh,0,255,THRESH_BINARY_INV | THRESH_OTSU);imshow("thresh", thresh);Mat mask;Mat kernel = getStructuringElement(MORPH_RECT, Size(31, 31));morphologyEx(thresh, mask, MORPH_CLOSE, kernel); // 形态学操作imshow("morphology", mask);vector<vector<Point>> contours;vector<Vec4i> hierarchy;findContours(mask, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);vector<vector<Point>> hull(contours.size());Mat drawing = Mat::zeros(mask.size(),CV_8UC3);for (size_t i = 0; i< contours.size(); i++) {double area = contourArea(contours[i]);if (area < 100) {continue;}convexHull(contours[i], hull[i], false);bool isHull = isContourConvex(contours[i]);cout << "isHull = " << isHull << endl;drawContours(drawing, contours, i, Scalar(0, 0, 255), 8, 8);drawContours(drawing, hull, i, Scalar(255, 0, 0), 8, 8);drawContours(src, hull, i, Scalar(255, 0, 0), 8, 8);}imshow("result",src);imshow("drawing",drawing);waitKey(0);return 0;
}

执行结果:

isHull = 0
isHull = 0
839c49a8d37aff3f5352be75dfbee988.jpeg
凸包分析1.png
f4655399d510ab8ae076c4096f2b890c.jpeg
凸包分析2.png

Part44. 总结

轮廓的基础特征是计算机视觉中的重要工具,这些特征可以应用于对象检测、形状识别、测量等各种应用场景。后续还会介绍更多的轮廓特征。

Java与Android技术栈】公众号

关注 Java/Kotlin 服务端、桌面端 、Android 、机器学习、端侧智能

更多精彩内容请关注:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/823746.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Java进阶(第八期): Java中递归的的使用和递归解决一些算法问题 Java中的异常机制、异常的处理逻辑 自定义异常

文章目录 一、递归1.1 递归的介绍1.2 递归的简单练习1.3 图解递归执行流程&#xff1a;1.4 使用递归完成悲波那契数列1.5 猴子吃桃子问题 二、异常三 、异常的处理逻辑3.1 try catch 捕获异常3.2 throws抛出异常 四、自定义异常 Java进阶&#xff08;第八期&#xff09; 一、递…

java三种注释方式

Java 中的注释有三种&#xff1a; 单行注释&#xff1a;通常用于解释方法内某单行代码的作用。 多行注释&#xff1a;通常用于解释一段代码的作用。 文档注释&#xff1a;通常用于生成 Java 开发文档。

re:Invent 2023技术上新|Amazon DynamoDB与OpenSearch Service的Zero-ETL集成

Amazon DynamoDB 与 Amazon OpenSearch Service 的 Zero-ETL 集成已正式上线&#xff0c;该服务允许您通过自动复制和转换您的 DynamoDB 数据来搜索数据&#xff0c;而无需自定义代码或基础设施。这种 Zero-ETL 集成减少了运营负担和成本&#xff0c;使您能够专注于应用程序。这…

Redis事务悄然而至:命令的背后故事

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Redis事务悄然而至&#xff1a;命令的背后故事 前言redis事务概述redis事务基础事务中的命令事务的一致性与隔离性事务的异常处理并发环境下的事务结语 前言 在数字化时代&#xff0c;数据处理的要求…

[C#]opencvsharp进行图像拼接普通拼接stitch算法拼接

介绍&#xff1a; opencvsharp进行图像拼一般有2种方式&#xff1a;一种是传统方法将2个图片上下或者左右拼接&#xff0c;还有一个方法就是融合拼接&#xff0c;stitch拼接就是一种非常好的算法。opencv里面已经有stitch拼接算法因此我们很容易进行拼接。 效果&#xff1a; …

Jmeter 性能测试 —— 评估一个系统TPS与并发数!

问题&#xff1a;性能压测&#xff0c;如何评估一个系统的TPS和并发数&#xff1f; 1、对于新系统 由业务部门或开发人员预估交易量和TPS指标 可以参考公式&#xff1a;并发用户 在线用户数 * 10%。 当一个系统还没有上线时&#xff0c;我们可以预判的是这个系统准备要给多…

一篇五分生信临床模型预测文章代码复现——Figure 10.机制及肿瘤免疫浸润(九)——ssGSEA——倒数第三节

之前讲过临床模型预测的专栏,但那只是基础版本,下面我们以自噬相关基因为例子,模仿一篇五分文章,将图和代码复现出来,学会本专栏课程,可以具备发一篇五分左右文章的水平: 本专栏目录如下: Figure 1:差异表达基因及预后基因筛选(图片仅供参考) Figure 2. 生存分析,…

车牌识别技术,如何用python识别车牌号

目录 一.前言 二.运行环境 三.代码 四.识别效果 五.参考 一.前言 车牌识别技术&#xff08;License Plate Recognition, LPR&#xff09;在交通计算机视觉&#xff08;Computer Vision, CV&#xff09;领域具有非常重要的研究意义。以下是该技术的一些扩展说明&#xff1…

VSCODE 修改Test模式下的的java jvm堆内存大小

在settings.json中添加如下语句 "java.test.config": {"vmArgs": ["-Xmx12G"]},

swing快速入门(三十二)消息对话框

注释很详细&#xff0c;直接上代码 上一篇 新增内容 1.自定义对话框前列图标 2.消息对话框的若干种形式 package swing21_30;import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent;public class swing_test_30 {// 定义一个JFrameJFrame jFrame n…

macos下转换.dmg文件为 .iso .cdr文件的简单方法

为了让镜像文件在mac 和windows平台通用, 所以需要将.dmg格式的镜像文件转换为.iso文件, 转换方法也非常简单, 一行命令即可 hdiutil convert /path/to/example.dmg -format UDTO -o /path/to/example.iso 转换完成后的文件名称默认是 example.iso.cdr 这里直接将.cdr后缀删…

自动驾驶学习笔记(二十三)——车辆控制模型

#Apollo开发者# 学习课程的传送门如下&#xff0c;当您也准备学习自动驾驶时&#xff0c;可以和我一同前往&#xff1a; 《自动驾驶新人之旅》免费课程—> 传送门 《Apollo开放平台9.0专项技术公开课》免费报名—>传送门 文章目录 前言 运动学模型 动力学模型 总结…

gradle下载太慢不用再烦恼了

编辑gradle-wrapper文件 使用腾讯镜像 https://mirrors.cloud.tencent.com/gradle/gradle-7.5-all.zip 来代替原来的 https\://services.gradle.org/distributions/gradle-7.5-all.zip

C#调用(python通过excel坐标生成的曲面地形图)案例

效果图: 文件图: 详解一:环境和python库问题 1.python 中只需要下载 matplotlib3.8.2和scipy1.11.4 2.我安装的python版本 详解二:解释器问题 python解释器这里有两种形式 第一种形式 1.调用 pycharm项目下的解释器,需要安装python必须的包(命令安装或者搜索安装)。 2.修改…

对“企业数据资源相关会计处理暂行规定“的个人理解

附&#xff1a;2023年数据资源入表白皮书下载&#xff1a; 关注WX公众号&#xff1a; commindtech77&#xff0c; 获得数据资产相关白皮书下载地址 1. 回复关键字&#xff1a;数据资源入表白皮书 下载 《2023数据资源入表白皮书》 2. 回复关键字&#xff1a;光大银行 下载 光…

Java项目:100SpringBoot图书管理系统

博主主页&#xff1a;Java旅途 简介&#xff1a;分享计算机知识、学习路线、系统源码及教程 文末获取源码 一、项目介绍 图书管理系统基于SpringBootMybatis开发&#xff0c;系统分为两种角色&#xff0c;分别是管理员和普通用户。 管理员功能如下&#xff1a; 书籍类别管理…

前端优化 - 防抖和节流

&#x1f4e2; 鸿蒙专栏&#xff1a;想学鸿蒙的&#xff0c;冲 &#x1f4e2; C语言专栏&#xff1a;想学C语言的&#xff0c;冲 &#x1f4e2; VUE专栏&#xff1a;想学VUE的&#xff0c;冲这里 &#x1f4e2; CSS专栏&#xff1a;想学CSS的&#xff0c;冲这里 &#x1f4…

驾驭AI助手,开启高效创作之旅:一篇文章,轻松搞定!

在信息爆炸的时代&#xff0c;内容创作已成为个人和企业的核心竞争力。但撰写一篇高质量的文章需要大量时间和精力。现在&#xff0c;有了AI助手&#xff0c;这些烦恼全部消失 首先&#xff0c;我们要进入首助编辑高手主页面&#xff0c;并在上方的板块栏里选择“AI文章创作”…

计算机组成原理之BCD码和奇偶校验码小白秒懂

BCD码简介 原文文档下载https://download.csdn.net/download/m0_46579394/88681870 BCD码也称二进码十进数&#xff0c;BCD码可分为有权码和无权码两类。其中&#xff0c;常见的有权BCD码有8421码、2421码、5421码&#xff0c;无权BCD码有余3码、余3循环码、格雷码。8421BCD码…

嵌入式科普(8)ESP-IDF newlib相关介绍和对比分析

一、目的/概述 二、资料来源 三、ESP-IDF简介 3.1 ESP-IDF FreeRTOS 3.2 ESP-IDF heap_caps 3.3 ESP-IDF newlib 四、对比 嵌入式科普(8)ESP-IDF newlib相关介绍和对比分析 一、目的/概述 1、在我的嵌入式科普(6)你听说过FreeRTOS heap6吗&#xff1f;…