Opencv+ROS实现颜色识别应用

embedded/2024/11/27 12:51:04/

目录

一、工具

二、原理

概念

本质

三、实践

 添加发布话题

主要代码

四、成果

五、总结


一、工具

opencv+ros

ubuntu18.04

摄像头

二、原理

概念

彩色图像:RGB(红,绿,蓝)

HSV图像:H(色调)S(饱和度)V(亮度)

色调(H:hue):用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,品红为300°;
饱和度(S:saturation):取值范围为0.0~1.0,值越大,颜色越饱和。
亮度(V:value):取值范围为0(黑色)~255(白色)。

但是在opencv中引用的范围有所不同,给出下表。

本质

颜色识别本质就是在图像上提取出你想要的颜色阈值,然后通过降噪优化模型,轮廓检测进行框选。

要点:

  • RGB转HSV
  • 所需颜色阈值(hsv),并二值化
  • 腐蚀操作除噪,Canny算法进行边缘检测
  • 最后通过findContours()函数找出轮廓坐标

三、实践

读取摄像头

    VideoCapture cap(video_device);  //dev/video0

RGB转HSV

cvtColor(frame, imghsv, COLOR_BGR2HSV);

直方图均衡化

split(imghsv, hsvSplit);
equalizeHist(hsvSplit[2], hsvSplit[2]);
merge(hsvSplit, imghsv);

直方图均衡化是一种简单有效的图像增强技术,用于增强动态范围偏小的图像的对比度

定义颜色阈值,这里选取红色

    Scalar lower_red(156, 43, 46);Scalar upper_red(180, 255, 255); // 定义红色的HSV范围inRange(imghsv, lower_red, upper_red, mask);//二值化红色部分

inRange()函数就是检测imghsv内所有像素是否在lower-upper之间,如果是则设为255,也就是白色。输出的是二值图。

用腐蚀,膨胀操作去噪点

  Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));morphologyEx(mask, mask, MORPH_OPEN, kernel);//开运算morphologyEx(mask, mask, MORPH_CLOSE, kernel);//闭运算

腐蚀,膨胀操作的对象是二值化图像

  • 腐蚀:变精细
  • 膨胀:变粗矿
  • 开运算:先腐蚀后膨胀 消去一个黑图中的很多小白点
  • 闭运算:先膨胀后腐蚀 消去一个白图中的很多小黑点
  • 梯度运算:膨胀-腐蚀

 高斯滤波,Canny边缘检测

    GaussianBlur(mask, mask, Size(3, 3), 0);//高斯滤波Canny(mask, mask, 100, 250);//canny算子边缘检测

 Canny()函数参数表明:

第一个:InputArray类型的image,输入图像
第二个:OutputArray类型的edges,输出的边缘图
第三个:double类型的threshold1,第一个滞后性阈值
第四个:double类型的threshold2,第二个滞后性阈值

 Canny过程为

  1. 高斯滤波获得平滑图像
  2. 计算每个像素点的梯度强度和方向
  3. 应用非极大值抑制,消除边缘检测带来的杂散响应
  4. 双阈值确定真实或潜在的边缘
  5. 抑制弱化边缘完成边缘检测

然后开始找轮廓

findContours()函数

findContours(mask,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE,Point());  

第一个参数:输入图像

第二个参数:所有轮廓

第三个参数:表示第i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号

第四个参数:RETR_EXTERNAL只检测最外围轮廓

第五个参数:CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息

寻找最大轮廓

 vector<double> Area(contours.size());//寻找最大面积的轮廓for (int i = 1; i < contours.size(); i++) {Area[i] = contourArea(contours[i]);if (Area[i] > Area[max]) {max = i;}   }Rect boundRect = boundingRect(Mat(contours[max]));circle(frame, Point(boundRect.x + boundRect.width/2, boundRect.y + boundRect.height/2), 5, Scalar(0,0,255), -1);

boundingRect()函数

表示包围轮廓的最大矩形

返回四个参数

第一个:boundRect.x

第二个:boundRect.y

第三个:boundRect.width

第四个:boundRect.hight

左上角顶点的像素坐标值及矩形边界的宽和高

然后将矩形在原画面画出即可

ROS_INFO("x:%d,y:%d",boundRect.x+ boundRect.width/2, boundRect.y + boundRect.height/2);
rectangle(frame, Point(boundRect.x, boundRect.y), Point(boundRect.x + boundRect.width, boundRect.y + boundRect.height), Scalar( 0, 0, 255), 2);

 添加发布话题

毕竟是在ros下编写的,我们要把像素坐标发布出去,这里自定义一个消息类型

boundingbox.msg

用来表示类和坐标值

主要代码

 while (ros::ok()) {  cap >> frame;  //摄像头画面赋给frameif(!frame.empty()) //画面是否正常{  /*对图片二次处理*/cvtColor(frame, imghsv, COLOR_BGR2HSV);// 将图像转换为HSV颜色空间split(imghsv, hsvSplit);equalizeHist(hsvSplit[2], hsvSplit[2]);merge(hsvSplit, imghsv);inRange(imghsv, lower_red, upper_red, mask);//二值化红色部分Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));morphologyEx(mask, mask, MORPH_OPEN, kernel);//开运算morphologyEx(mask, mask, MORPH_CLOSE, kernel);//闭运算GaussianBlur(mask, mask, Size(5, 5), 0);//高斯滤波Canny(mask, mask, 150, 100);//canny算子边缘检测vector<vector<Point> > contours;vector<Vec4i> hierarchy;findContours(mask,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE,Point());  //ROS_INFO("个数为%d",int(contours.size()));vector<double> Area(contours.size());if(contours.size() > 0 ){//寻找最大面积的轮廓for (int i = 1; i < contours.size(); i++) {Area[i] = contourArea(contours[i]);if (Area[i] > Area[max]) {max = i;}   }Rect boundRect = boundingRect(Mat(contours[max]));circle(frame, Point(boundRect.x + boundRect.width/2, boundRect.y + boundRect.height/2), 5, Scalar(0,0,255), -1);ROS_INFO("x:%d,y:%d",boundRect.x+ boundRect.width/2, boundRect.y + boundRect.height/2);rectangle(frame, Point(boundRect.x, boundRect.y), Point(boundRect.x + boundRect.width, boundRect.y + boundRect.height), Scalar( 0, 0, 255), 2);detect_msg.Class = "red";detect_msg.xmin = boundRect.x;detect_msg.xmax=boundRect.x + boundRect.width;detect_msg.ymin=boundRect.y;detect_msg.ymax= boundRect.y + boundRect.height;}

四、成果

运行画面

 查看话题

这里识别画面内所有红色区域

五、总结

写代码过程中还是遇到很多问题的,不知道是opencv版本不兼容的问题还是哪里我编写不细致,节点总是挂掉。

但还是能完成基本需求。

这里把报错留下,希望有大佬能帮帮我

OpenCV Error: Assertion failed (npoints >= 0 && (depth == CV_32F || depth == CV_32S)) in pointSetBoundingRect, file /build/opencv-L2vuMj/opencv-3.2.0+dfsg/modules/imgproc/src/shapedescr.cpp, line 466
terminate called after throwing an instance of 'cv::Exception'
  what():  /build/opencv-L2vuMj/opencv-3.2.0+dfsg/modules/imgproc/src/shapedescr.cpp:466: error: (-215) npoints >= 0 && (depth == CV_32F || depth == CV_32S) in function pointSetBoundingRect

应该是boundingRect()函数的问题,但不知道问题在哪

欢迎评论区指正。


http://www.ppmy.cn/embedded/140901.html

相关文章

构建与优化数据仓库-实践指南

数仓构建流程 下图为MaxCompute数据仓库构建的整体流程。 基本概念 在正式学习本教程之前&#xff0c;您需要首先理解以下基本概念&#xff1a; 业务板块&#xff1a;比数据域更高维度的业务划分方法&#xff0c;适用于庞大的业务系统。 维度&#xff1a;维度建模由Ralph Ki…

数据结构--AVL树(平衡二叉树)

✅博客主页:爆打维c-CSDN博客​​​​​​ &#x1f43e; &#x1f539;分享c、c知识及代码 &#x1f43e; &#x1f539;Gitee代码仓库 五彩斑斓黑1 (colorful-black-1) - Gitee.com 一、AVL树是什么&#xff1f;&#xff08;含义、性质&#xff09; 1.AVL树的概念 AVL树是最…

Linux sed基本命令

sed基础 流编辑器&#xff08;编辑器&#xff1a;进行增删改查动作&#xff09;&#xff0c;可以对sed每一行进行增删改查 sed [-option] 模式 操作指令 文件名 示例&#xff1a; 第三行会重复打印一次 P&#xff08;打印操作&#xff09; 自上而下&#xff0c;一行一行读…

pikachu平台xss漏洞详解

声明&#xff1a;文章只是起演示作用&#xff0c;所有涉及的网站和内容&#xff0c;仅供大家学习交流&#xff0c;如有任何违法行为&#xff0c;均和本人无关&#xff0c;切勿触碰法律底线 文章目录 概述&#xff1a;什么是xss一、反射型XSS1. get2. post 二、存储型XSS三、DOM…

Sui 链游戏开发实战:用 Move 写一个链上剪刀石头布游戏!

系列文章目录 Task1&#xff1a;hello move&#x1f6aa; Task2&#xff1a;move coin&#x1f6aa; Task3&#xff1a;move nft&#x1f6aa; Task4&#xff1a;move game&#x1f6aa; 更多精彩内容&#xff0c;敬请期待&#xff01;✌️ 文章目录 系列文章目录前言什么是 …

Scala:根据身份证号码,输出这个人的籍贯

例如&#xff1a;42表示湖北&#xff0c;33表示浙江&#xff0c;11表示北京&#xff0c;31表示上海 object y8 {def main(args: Array[String]): Unit {val id"429005200101020054"val proviceid.substring(0,2) // println(provice) // if (provice"42…

《网络是怎样连接的》整体的总结

1、前言 本篇是对读完《网络是怎样连接的》这本书的一篇整的总结&#xff0c;具体的每一章节的学习记录之前已经有发布过&#xff01;然后呢&#xff0c;还会结合本书的内容也大概总结一下外网进内网的一般步骤&#xff0c;其中会经过哪些环节&#xff0c;在下一章发布。有错误…

网络层协议IP

对于网络层我们直接通过IP协议来了解其内容 一.IP协议 首先我们先来了解几个概念&#xff1a; 主机&#xff1a;配有IP地址&#xff0c;但是不进行路由控制的设备 路由器&#xff1a;配有IP地址&#xff0c;同时进行路由控制的设备 节点&#xff1a;主机和路由器的统称 所以现在…