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

news2024/12/26 21:48:12

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

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

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

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.coloradmin.cn/o/1893482.html

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

相关文章

农作物生长环境的远程监控与智能调控

农作物生长环境的远程监控与智能调控 农作物生长环境的远程监控与智能调控技术&#xff0c;作为现代农业科技的核心组成部分&#xff0c;正逐步革新传统农业的生产模式&#xff0c;推动农业向精准化、智能化转型。这一技术体系综合应用了物联网、大数据、云计算以及人工智能等…

医疗器械进销存软件 专业合规的医疗公司器械出入库管理软件

财务管理&#xff1a;财务档案统一管理&#xff0c;有利于科学管理企业资金 财务管理&#xff1a;发票关联业务单据&#xff0c;业财融合&#xff0c;加速财务数字化转型 财务管理&#xff1a;提供收付款功能&#xff0c;加快企业应收账款的回收&#xff0c;降低付款的资金浮…

数据融合平台的概述、特点及技术方案

在当今数字化时代&#xff0c;数据已成为企业最宝贵的资产之一。然而&#xff0c;数据的分散存储和格式不一&#xff0c;常常导致数据孤岛现象&#xff0c;使得数据的潜在价值难以被充分挖掘和利用。在这样的背景下&#xff0c;数据融合平台应运而生&#xff0c;它的意义不仅在…

“第六感”真的存在吗?

现在已有证据表明&#xff0c;人类除视觉、听觉、嗅觉、味觉和触觉五种感觉以外&#xff0c;确实存在“第六感” “第六感”的学术名称为“超感自知觉”(简称ESP)&#xff0c;它能透过正感官之外的渠道接收信息&#xff0c; 预知将要发生的事&#xff0c;而且与当事人之前的经…

Windows的管理工具

任务计划程序&#xff1a;这是一个用来安排任务自动运行的工具。你可以在这里创建新的任务&#xff0c;设定触发条件&#xff0c;并指定任务的操作。 事件查看器&#xff1a;这是一套日志记录和分析工具&#xff0c;&#xff0c;你可以了解到系统的工作状况&#xff0c;帮助诊…

蓝蜂网关接入雄安新区物联网统一开放平台应用案例

蓝蜂网关接入雄安新区物联网统一开放平台案例 一、应用背景 为响应国家《河北雄安新区规划纲要》&#xff0c;由中国雄安集团数字城市科技有限公司牵头&#xff0c;以中移物联网有限公司为牵头单位的联合体&#xff0c;构建了雄安新区物联网统一开放平台&#xff08;简称雄安…

Anaconda安装及配置+pytorch深度学习环境(2024复旦计算机工作站0704)

目录 前言 一、Anaconda的下载与安装 二、检查是否有Nvidia显卡 三、安装CPU环境的pytorch 四、安装GPU环境的pytorch&#xff08;待写...) 五、Anaconda与Pycharm 配置 总结 前言 深度学习越来越火啦&#xff0c;深入到各行各业&#xff0c;小北个人也对深度学习很感兴…

Day05-讲师列表前端-讲师信息添加

代码&#xff1a; //添加讲师 addTeacher(teacher){ return request({ url:/eduservice/teacher/addTeacher, method:‘post’, data:teacher }) } &#xff08;2&#xff09;在页面实现调用 代码&#xff1a; 讲师添加 <el-button type“primary” :disabled“sav…

Python代码设置Excel工作表背景色或背景图

Excel是工作中数据处理和分析数据的重要工具。面对海量的数据和复杂的表格&#xff0c;如何提高工作效率、减少视觉疲劳并提升数据的可读性是不容忽视的问题。而给工作表设置合适的背景是表格优化的一个有效方式。为Excel工作表设置背景色或背景图不仅能够美化工作表&#xff0…

类的动态加载-双亲委派模型

java反射基础 Java 基础 - 反射机制详解 | Java 全栈知识体系 (pdai.tech) 类的动态加载 参考链接&#xff1a;类的动态加载 构造是和实例化也就是对象相关的。 静态代码块是在初始化的时候就调用的 Class.forName();就会调用静态代码块 forName&#xff0c;加载类时默认…

【Mac】draw.io for Mac(流程图绘制工具)及同类型软件介绍

软件介绍 Draw.io&#xff08;现在称为diagrams.net&#xff09;是一个流行的开源图表软件&#xff0c;可以帮助用户创建各种类型的图表和图形&#xff0c;如流程图、组织结构图、网络图、UML图、平面设计图等等。它最初作为一个Web应用程序推出&#xff0c;后来也推出了桌面版…

推荐一个私有化部署的物联网平台

引言 随着物联网技术的飞速发展&#xff0c;越来越多的企业开始寻求能够提供稳定、安全、可定制的物联网解决方案。私有化部署的物联网平台因其能够满足企业对数据安全和个性化需求的优势&#xff0c;逐渐成为市场的新宠。本文将详细介绍ThingsKit物联网平台&#xff0c;一个专…

代谢组数据分析(十二):岭回归、Lasso回归、弹性网络回归构建预测模型

欢迎大家关注全网生信学习者系列: WX公zhong号:生信学习者Xiao hong书:生信学习者知hu:生信学习者CDSN:生信学习者2介绍 在代谢物预测模型的构建中,我们采用了三种主流的回归分析方法:岭回归、Lasso回归以及弹性网络回归。这三种方法各有其独特的原理和适用场景,因此在…

pnpm的坑

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

C语言自定义类型(结构体,枚举,联合):

大家好久不见&#xff0c;今天我们来学习一下C语言中的自定义类型&#xff1a; C语言的自定义类型包括&#xff1a;结构体&#xff0c;枚举和联合&#xff0c;接下来大家跟我来一起认识一下这三种类型。 目录 1. 结构体 1.1.1 结构体类型的声明 1.1.2 结构的特殊声明 1.1…

nginx的重定向(rewrite)

nginx的重定向&#xff08;rewrite&#xff09; location 匹配 location匹配的就是后面的URI /wordpress 192.168.60.20/wordpress location匹配的分类和优先级 1、精确匹配 location / 对字符串进行完全匹配&#xff0c;必须完全符合 2、正则匹配 ^~ 前缀匹配&#x…

为本地化准备营销材料的几个步骤

为本地化准备营销材料涉及几个关键步骤&#xff0c;以确保内容在文化上合适、语言上准确&#xff0c;并与目标受众相关。以下是五个基本步骤&#xff1a; 进行市场调查 了解目标市场至关重要。进行深入研究&#xff0c;以收集有关目标地区受众的文化细微差别、消费者行为、地…

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

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

MySQL之聚簇索引和非聚簇索引

1、什么是聚簇索引和非聚簇索引&#xff1f; 聚簇索引&#xff0c;通常也叫聚集索引。 非聚簇索引&#xff0c;指的是二级索引。 下面看一下它们的含义&#xff1a; 1.1、聚集索引选取规则 如果存在主键&#xff0c;主键索引就是聚集索引。如果不存在主键&#xff0c;将使…

高校搭建AIGC新媒体实验室,创新新闻教育教学模式

高校作为人才培养的重要阵地&#xff0c;必须紧跟时代步伐&#xff0c;不断创新教育教学模式&#xff0c;提升跨界融合育人水平&#xff0c;通过AIGC新媒体实验室探索创新人才培养模式。AIGC新媒体实验室不仅能够高效赋能高校宣传媒体矩阵&#xff0c;也可以助力教学实践与AIGC…