轮廓检测(python和c++实现)

利用轮廓检测,我们可以检测物体的边界,并在图像中轻松定位。这通常是许多有趣应用的第一步,如图像前景提取、简单图像分割、检测和识别。

无人看管物体检测:公共场所的任何无人看管物体一般都被视为可疑物体。一种有效而安全的解决方案是:(利用背景抽取法形成轮廓,检测无人看管物体)

背景/前景分割:要将一幅图像的背景替换为另一幅图像的背景,需要执行图像前景提取(类似于图像分割)。使用轮廓线是进行分割的一种方法。更多详情,请参阅本帖。以下图片展示了此类应用的简单示例:

1、什么是轮廓线

当我们将物体边界上的所有点连接起来时,就得到了一条轮廓线。通常,一个特定的轮廓是指具有相同颜色和强度的边界像素。OpenCV 可以非常方便地在图像中查找和绘制轮廓线。它提供了两个简单的函数:

  1. findContours()
  2. drawContours()

此外,它还有两种不同的轮廓检测算法:

  1. CHAIN_APPROX_SIMPLE
  2. CHAIN_APPROX_NONE

我们将在下面的示例中详细介绍这些算法。下图展示了这些算法如何检测简单物体的轮廓。

2、在 OpenCV 中检测和绘制轮廓的步骤

1.读取图像并将其转换为灰度格式 。

将图像转换为灰度格式非常重要,因为这将为下一步做好准备。将图像转换为单通道灰度图像对于阈值处理非常重要,而阈值处理又是轮廓检测算法正常工作的必要条件。

2.应用二进制阈值处理
在寻找轮廓时,首先要对灰度图像应用二进制阈值或 Canny 边缘检测。在这里,我们将应用二进制阈值处理。将图像转换为黑白图像,突出显示感兴趣的物体,便于轮廓检测算法处理。阈值处理使图像中物体的边界完全变白,所有像素都具有相同的强度。现在,算法可以从这些白色像素中检测出物体的边界。注:值为 0 的黑色像素被视为背景像素,会被忽略。

这一步可能会出现一个问题。如果我们使用的是 R(红)、G(绿)或 B(蓝)等单通道图像,而不是灰度(阈值)图像,该怎么办?在这种情况下,轮廓检测算法将无法正常工作。正如我们之前所讨论的,该算法会寻找边界和相似强度的像素来检测轮廓。二值图像比单一(RGB)彩色通道图像更能提供这些信息。在本博客的后面部分,我们将介绍仅使用单一 R、G 或 B 通道而非灰度和阈值图像时的结果图像。

3.查找轮廓

使用 findContours() 函数检测图像中的轮廓。

4.在原始 RGB 图像上绘制轮廓线。 

确定轮廓后,使用 drawContours() 函数在原始 RGB 图像上叠加轮廓。

3、使用Opencv进行查找和绘制轮廓

(1)二值化处理

Python:

import cv2
 
# read the image
image = cv2.imread('input/image.jpg')
	
# convert the image to grayscale format
img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# apply binary thresholding
ret, thresh = cv2.threshold(img_gray, 150, 255, cv2.THRESH_BINARY)
# visualize the binary image
cv2.imshow('Binary image', thresh)
cv2.waitKey(0)
cv2.imwrite('image_thres.jpg', thresh)
cv2.destroyAllWindows()

使用 threshold() 函数对图像应用二进制阈值。任何值大于 150 的像素都将被设置为 255(白色)。生成图像中的所有剩余像素都将设置为 0(黑色)。阈值 150 是一个可调整的参数,因此您可以尝试使用。

C++:

#include<opencv2/opencv.hpp>
#include <iostream>
 
using namespace std;
using namespace cv;
 
int main() {
   // read the image
   Mat image = imread("input/image_1.jpg");
  // convert the image to grayscale format
  Mat img_gray;
  cvtColor(image, img_gray, COLOR_BGR2GRAY);
  // apply binary thresholding
  Mat thresh;
  threshold(img_gray, thresh, 150, 255, THRESH_BINARY);
  imshow("Binary mage", thresh);
  waitKey(0);
  imwrite("image_thres1.jpg", thresh);
  destroyAllWindows();
}

(2)轮廓查找

从 findContours() 函数开始。该函数有三个必备参数。

- image:上一步获得的二进制输入图像image。

- mode:模式:这是轮廓检索模式。我们提供的是 RETR_TREE,这意味着算法将从二值图像中检索所有可能的轮廓。还有更多的轮廓检索模式,我们也将一一讨论。有关这些选项的更多详情,请点击此处。 

- method:定义轮廓逼近方法。在本例中,我们将使用 CHAIN_APPROX_NONE。虽然这种方法比 CHAIN_APPROX_SIMPLE 稍慢,但我们将使用这种方法存储所有轮廓点

(3)绘制轮廓

使用 drawContours() 函数将轮廓叠加到 RGB 图像上。该函数有四个必填参数和几个可选参数。以下前四个参数为必填参数。

-image(图像):这是输入的 RGB 图像,您要在上面绘制轮廓。

- contours(轮廓线):表示从 findContours() 函数中获取的轮廓。

- contourIdx:轮廓线点的像素坐标,列在获取的轮廓线中。使用此参数,您可以指定列表中的索引位置,以准确显示您要绘制的轮廓点。提供负值将绘制所有轮廓点。

- color:表示要绘制的轮廓点的颜色。我们将绘制绿色的点。

- thickness (厚度):这是轮廓点的厚度。

Python:

# detect the contours on the binary image using cv2.CHAIN_APPROX_NONE
contours, hierarchy = cv2.findContours(image=thresh, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)
                                      
# draw contours on the original image
image_copy = image.copy()
cv2.drawContours(image=image_copy, contours=contours, contourIdx=-1, color=(0, 255, 0), thickness=2, lineType=cv2.LINE_AA)
                
# see the results
cv2.imshow('None approximation', image_copy)
cv2.waitKey(0)
cv2.imwrite('contours_none_image1.jpg', image_copy)
cv2.destroyAllWindows()

C++:

// detect the contours on the binary image using cv2.CHAIN_APPROX_NONE
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(thresh, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);
// draw contours on the original image
Mat image_copy = image.clone();
drawContours(image_copy, contours, -1, Scalar(0, 255, 0), 2);//-1绘制所有轮廓点
imshow("None approximation", image_copy);
waitKey(0);
imwrite("contours_none_image1.jpg", image_copy);
destroyAllWindows();

4、使用单通道:红、绿或蓝图像进行轮廓检测

以下是在检测轮廓时分别使用红、绿、蓝通道的一些结果,仅供参考。我们在之前的轮廓检测步骤中讨论过这个问题。以下是同一幅图像的 Python 和 C++ 代码。

Python:

import cv2
 
# read the image
image = cv2.imread('input/image.jpg')
 
# B, G, R channel splitting
blue, green, red = cv2.split(image)
 
# detect contours using blue channel and without thresholding
contours1, hierarchy1 = cv2.findContours(image=blue, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)
 
# draw contours on the original image
image_contour_blue = image.copy()
cv2.drawContours(image=image_contour_blue, contours=contours1, contourIdx=-1, color=(0, 255, 0), thickness=2, lineType=cv2.LINE_AA)
# see the results
cv2.imshow('Contour detection using blue channels only', image_contour_blue)
cv2.waitKey(0)
cv2.imwrite('blue_channel.jpg', image_contour_blue)
cv2.destroyAllWindows()
 
# detect contours using green channel and without thresholding
contours2, hierarchy2 = cv2.findContours(image=green, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)
# draw contours on the original image
image_contour_green = image.copy()
cv2.drawContours(image=image_contour_green, contours=contours2, contourIdx=-1, color=(0, 255, 0), thickness=2, lineType=cv2.LINE_AA)
# see the results
cv2.imshow('Contour detection using green channels only', image_contour_green)
cv2.waitKey(0)
cv2.imwrite('green_channel.jpg', image_contour_green)
cv2.destroyAllWindows()
 
# detect contours using red channel and without thresholding
contours3, hierarchy3 = cv2.findContours(image=red, mode=cv2.RETR_TREE, method=cv2.CHAIN_APPROX_NONE)
# draw contours on the original image
image_contour_red = image.copy()
cv2.drawContours(image=image_contour_red, contours=contours3, contourIdx=-1, color=(0, 255, 0), thickness=2, lineType=cv2.LINE_AA)
# see the results
cv2.imshow('Contour detection using red channels only', image_contour_red)
cv2.waitKey(0)
cv2.imwrite('red_channel.jpg', image_contour_red)
cv2.destroyAllWindows()

C++:

#include<opencv2/opencv.hpp>
#include <iostream>
 
using namespace std;
using namespace cv;
 
int main() {
   // read the image
   Mat image = imread("input/image_1.jpg");
 
   // B, G, R channel splitting
   Mat channels[3];
   split(image, channels);
 
   // detect contours using blue channel and without thresholding
   vector<vector<Point>> contours1;
   vector<Vec4i> hierarchy1;
   findContours(channels[0], contours1, hierarchy1, RETR_TREE, CHAIN_APPROX_NONE);
   // draw contours on the original image
   Mat image_contour_blue = image.clone();
   drawContours(image_contour_blue, contours1, -1, Scalar(0, 255, 0), 2);
   imshow("Contour detection using blue channels only", image_contour_blue);
   waitKey(0);
   imwrite("blue_channel.jpg", image_contour_blue);
   destroyAllWindows();
 
   // detect contours using green channel and without thresholding
   vector<vector<Point>> contours2;
   vector<Vec4i> hierarchy2;
   findContours(channels[1], contours2, hierarchy2, RETR_TREE, CHAIN_APPROX_NONE);
   // draw contours on the original image
   Mat image_contour_green = image.clone();
   drawContours(image_contour_green, contours2, -1, Scalar(0, 255, 0), 2);
   imshow("Contour detection using green channels only", image_contour_green);
   waitKey(0);
   imwrite("green_channel.jpg", image_contour_green);
   destroyAllWindows();
 
   // detect contours using red channel and without thresholding
   vector<vector<Point>> contours3;
   vector<Vec4i> hierarchy3;
   findContours(channels[2], contours3, hierarchy3, RETR_TREE, CHAIN_APPROX_NONE);
   // draw contours on the original image
   Mat image_contour_red = image.clone();
   drawContours(image_contour_red, contours3, -1, Scalar(0, 255, 0), 2);
   imshow("Contour detection using red channels only", image_contour_red);
   waitKey(0);
   imwrite("red_channel.jpg", image_contour_red);
   destroyAllWindows();
}

实验结果可见,我们使用红蓝绿单通道图像应用轮廓检测算法时,无法正确找到轮廓。这是因为它无法正确检测到物体的边界,而且像素之间的强度差异也不明确。因此,我们更倾向于使用灰度图像和二进制阈值图像来检测轮廓。

 findContours() 方法CHAIN_APPROX_SIMPLE和 CHAIN_APPROX_NONE。

CHAIN_APPROX_SIMPLE 算法会压缩轮廓线上的水平、垂直和对角线线段,只留下它们的端点。这意味着沿直线路径的任何点都将被忽略,我们将只留下端点。例如,沿矩形绘制一条轮廓线。除了四个角点外,所有轮廓点都将被忽略。这种方法比 CHAIN_APPROX_NONE 更快,因为算法无需存储所有点,使用的内存更少,因此执行时间更短。

如果仔细观察,CHAIN_APPROX_NONE 和 CHAIN_APPROX_SIMPLE 的输出几乎没有差别。  这是为什么呢?这要归功于 drawContours() 函数。虽然 CHAIN_APPROX_SIMPLE 方法通常会产生较少的点,但 drawContours() 函数会自动连接相邻的点,即使它们不在轮廓线列表中,也会将它们连接起来。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一文搞懂MySsql的Buffer Pool

Buffer Pool是什么 Buffer Pool是MySQL数据库中一个非常关键的组件。数据库中的数据最终都是存放在磁盘文件上的。但是在对数据库执行增删改查操作时&#xff0c;不可能直接更新磁盘上的数据。因为如果直接对磁盘进行随机读写操作&#xff0c;那速度是相当的慢的。随便一个大磁…

联系表单提交后,自动发送邮件的实现方法?

联系表单自动邮件回执功能怎么样&#xff1f;如何设置邮件回执&#xff1f; 联系表单是用户与网站所有者沟通的重要渠道。为了提升用户体验&#xff0c;许多网站会在联系表单提交后自动发送确认邮件给用户。AokSend将探讨如何实现这一功能&#xff0c;介绍不同的方法和步骤&am…

如何在 Selenium Python 中解决验证码 | 2024 完整指南

由于在进行网络自动化时遇到验证码是让许多人感到不知所措的问题。这些验证码专为区分人类用户和自动化脚本而设计&#xff0c;对于使用Selenium进行网络爬虫或自动化任务而言&#xff0c;无疑是一个巨大的挑战。2024年的完全指南将为您提供全面的解决方案&#xff0c;帮助您高…

LabVIEW新能源汽车电池性能测试系统

新能源汽车的核心部件之一是电池&#xff0c;其性能直接关系到整车的续航里程、安全性和寿命。为了确保电池的性能和可靠性&#xff0c;测试是必不可少的环节。本文介绍了一种基于LabVIEW的新能源汽车电池性能测试系统&#xff0c;通过LabVIEW与数据采集设备的无缝集成&#xf…

【Spring Boot 源码学习】初识 ConfigurableEnvironment

《Spring Boot 源码学习系列》 初识 ConfigurableEnvironment 一、引言二、主要内容2.1 Environment2.1.1 配置文件&#xff08;profiles&#xff09;2.1.2 属性&#xff08;properties&#xff09; 2.2 ConfigurablePropertyResolver2.2.1 属性类型转换配置2.2.2 占位符配置2.…

docker容器技术、k8s的原理和常见命令、用k8s部署应用步骤

容器技术 容器借鉴了集装箱的概念&#xff0c;集装箱解决了什么问题呢&#xff1f;无论形状各异的货物&#xff0c;都可以装入集装箱&#xff0c;集装箱与集装箱之间不会互相影响。由于集装箱是标准化的&#xff0c;就可以把集装箱整齐摆放起来&#xff0c;装在一艘大船把他们…

C++基础(五):类和对象(上)

从今天开始&#xff0c;我们正式进入面向对象编程&#xff0c;这是C与C语言的重要区别&#xff0c;编程思想发生变化&#xff0c;那到底什么是面向对象编程呢&#xff1f;接下来&#xff0c;我们慢慢的深入学习。 目录 一、面向过程和面向对象初步认识 1.1 面向过程 1.2 面…

【Python】变量与基本数据类型

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️Python】 文章目录 前言变量声明变量变量的命名规则 变量赋值多个变量赋值 标准数据类型变量的使用方式存储和访问数据&#xff1a;参与逻辑运算和数学运算在函数间传递数据构建复杂的数据结构 NameE…

腾讯混元文生图开源模型推出小显存版本,6G显存即可运行,并开源caption模型

7月4日&#xff0c;腾讯混元文生图大模型&#xff08;混元DiT&#xff09;宣布开源小显存版本&#xff0c;仅需6G显存即可运行&#xff0c;对使用个人电脑本地部署的开发者十分友好&#xff0c;该版本与LoRA、ControlNet等插件&#xff0c;都已适配至Diffusers库&#xff1b;并…

达梦数据库 页大小与数据库字段长度的关系

对于达梦数据库实例而言&#xff0c;页大小 (page_size)、簇大小 (extent_size)、大小写敏感 (case_sensitive)、字符集 (charset) 这四个参数&#xff0c;一旦确定无法修改&#xff1b;如果过程中发现这些数据设置的不对&#xff0c;只能是重新新建数据库实例&#xff0c;而不…

脑启发设计:人工智能的进化之路

编者按&#xff1a;你可以用左手&#xff08;不常用的那只手&#xff09;的小指与食指拿起一件物品么&#xff1f; 试完你是不是发现自己竟然可以毫不费力地用自己不常用的手中&#xff0c;两根使用频率相对较低的手指&#xff0c;做一个不常做的动作。这就是人类大脑不可思议…

14-5 小语言模型SLM 百科全书

想象一下这样一个世界&#xff1a;智能助手不再驻留在云端&#xff0c;而是驻留在你的手机上&#xff0c;无缝理解你的需求并以闪电般的速度做出响应。这不是科幻小说&#xff1b;这是小型语言模型 (SLM) 的前景&#xff0c;这是一个快速发展的领域&#xff0c;有可能改变我们与…

台灯学生用哪个牌子最好?学生用台灯品牌排行榜分析

台灯学生用哪个牌子最好&#xff1f;护眼台灯在近年来成为家长和长时间使用电子设备人群关注的家电/学生产品。对于家中有孩子或经常面对电子屏幕的人士来说&#xff0c;很多人可能已经对这类产品有所了解并进行了购买。然而&#xff0c;部分家长对护眼台灯的认识还不够深入&am…

windows安装jdk21

下载 下载zip解压 设置环境变量 设置JAVA_HOME环境变量 Path环境变量添加如下值%HAVA_HOME%\bin 打开新的cmd&#xff0c;输入java --version查看效果

CentralCache中心缓存

目录 一.CentralCache基本结构 1.CentralCache任务 2.基本结构 二.函数调用层次结构/.h文件 三.Span和SpanList的封装 Span:大块内存跨度 PAGE_ID _pageId size_t _objSize _useCount SpanList:管理Span的双链表(桶锁) 四.获取大块内存GetOneSpan 五.FetchRangeObj输…

源代码防泄漏之反向沙箱方案的经验分享

反向沙箱&#xff08;Reverse Sandbox&#xff09;是一种安全技术&#xff0c;主要用于检测和分析恶意软件的行为。与传统沙箱不同&#xff0c;反向沙箱的重点在于模拟恶意软件的预期运行环境&#xff0c;以诱导恶意软件展示其真实行为。这种技术可以帮助安全专家更深入地理解恶…

四川蔚澜时代电子商务有限公司打造抖音电商服务新高地

在数字化浪潮汹涌澎湃的今天&#xff0c;电商行业以其独特的魅力和强大的市场潜力&#xff0c;成为了推动经济增长的新引擎。四川蔚澜时代电子商务有限公司&#xff0c;作为这个领域的佼佼者&#xff0c;正以其专业的服务、创新的理念和卓越的实力&#xff0c;引领抖音电商服务…

【Linux进阶】Linux目录配置,FHS

在了解了每个文件的相关种类与属性&#xff0c;以及了解了如何修改文件属性与权限的相关信息后&#xff0c;再来要了解的就是&#xff0c;为什么每个Linux发行版它们的配置文件、执行文件、每个目录内放置的东西&#xff0c;其实都差不多&#xff1f;原来是有一套标准依据&…

在 Mac 上使用 MLX 微调微软 phi3 模型

微调大语言模型是常见的需求&#xff0c;由于模型参数量大&#xff0c;即使用 Lora/Qlora 进行微调也需要 GPU 显卡&#xff0c;Mac M系是苹果自己的 GPU&#xff0c;目前主流的框架还在建立在 CUDA 的显卡架构&#xff0c;也就是主要的卡还是来自英伟达。如果要用 Mac 来做训练…

pnpm的坑

请问pnpm的两个坑怎么解决&#xff1a; 第一个坑&#xff1a;没有节省磁盘空间 我已经配置了依赖的存储位置&#xff0c; 但我在项目里pnpm install以后&#xff0c;发现依赖包还是很大&#xff0c; 然后发现里面的链接并不是指向先前配置的依赖存储位置&#xff0c;而是指…