ROS中用opencv库实现物体中心点提取

2023-05-16

老师搞了个很简陋的双目摄像头,只能输出两个摄像头的图像,所以为了提取定位物体中心坐标,还得算个深度距离。先对两个摄像头图像处理一下,基于阈值分割,然后提取个轮廓,计算个质心,水平实在有限,提取的精度不高。
这是左右摄像头发布二维坐标的程序

#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <cv_bridge/cv_bridge.h>
#include <sensor_msgs/image_encodings.h>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <geometry_msgs/Point.h> //点 消息类型

static const std::string OPENCV_WINDOW_second = "Raw Image window";
static const std::string OPENCV_WINDOW_left_second = "Raw Image window_left";
//static const std::string OPENCV_WINDOW_1 = "Edge Detection";
using namespace cv;
using namespace std;
class Edge_Detector_second
{
  ros::NodeHandle nh_;
  image_transport::ImageTransport it_;
  image_transport::Subscriber image_sub_second;
  image_transport::Subscriber image_sub_left_second;
  image_transport::Publisher image_pub_second;
  ros::Publisher leftPointPub_second;
  image_transport::Publisher image_pub_left_second;
  ros::Publisher rightPointPub_second;


public:
  Edge_Detector_second()
    : it_(nh_)
  {
    // Subscribe to input video feed and publish output video feed
    image_sub_second = it_.subscribe("/right/image_raw", 1,
      &Edge_Detector_second::right_imageCb_second, this);
    image_sub_left_second= it_.subscribe("/left/image_raw", 1,
      &Edge_Detector_second::left_imageCb_second, this);
    image_pub_second= it_.advertise("/edge_detector/right_image_second", 1);
    image_pub_left_second = it_.advertise("/edge_detector/left_image_second", 1);
    leftPointPub_second = nh_.advertise<geometry_msgs::Point>("/edge_detector/left_2Dposition_second", 1); //发布话题:left_2Dposition
    rightPointPub_second = nh_.advertise<geometry_msgs::Point>("/edge_detector/right_2Dposition_second", 1); //发布话题:right_2Dposition
    //cv::namedWindow(OPENCV_WINDOW);

  }

  ~Edge_Detector_second()
  {
    cv::destroyWindow(OPENCV_WINDOW_second);
    cv::destroyWindow(OPENCV_WINDOW_left_second);

  }

//回调函数
  void right_imageCb_second(const sensor_msgs::ImageConstPtr& msg)
  {
      geometry_msgs::Point rightObjectCenter_second;

    cv_bridge::CvImagePtr cv_ptr;
    namespace enc = sensor_msgs::image_encodings;

    try
    {
      cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);
    }
    catch (cv_bridge::Exception& e)
    {
      ROS_ERROR("cv_bridge exception: %s", e.what());
      return;
    }

    // Draw an example circle on the video stream
    if (cv_ptr->image.rows > 200 && cv_ptr->image.cols > 300){
        RotatedRect rightFaceRectangle = detect_edges_second(cv_ptr->image);


   // detect_edges(cv_ptr->image);
Mat imag=cv_ptr->image;

        image_pub_second.publish(cv_ptr->toImageMsg());
        rightObjectCenter_second.x = rightFaceRectangle.center.x;
        rightObjectCenter_second.y = rightFaceRectangle.center.y;
      rightPointPub_second.publish(rightObjectCenter_second); //发布得到的目标坐标点
        cv::imshow(OPENCV_WINDOW_second, imag);
        cv::waitKey(3);

    }
  }
  //left
  void left_imageCb_second(const sensor_msgs::ImageConstPtr& msg)
  {
      geometry_msgs::Point leftObjectCenter_second;

    cv_bridge::CvImagePtr cv_ptr;
    namespace enc = sensor_msgs::image_encodings;

    try
    {
      cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);
    }
    catch (cv_bridge::Exception& e)
    {
      ROS_ERROR("cv_bridge exception: %s", e.what());
      return;
    }

    // Draw an example circle on the video stream
    if (cv_ptr->image.rows > 200 && cv_ptr->image.cols > 300){
        RotatedRect leftFaceRectangle = detect_edges_second(cv_ptr->image);

    //detect_edges(cv_ptr->image);
        image_pub_left_second.publish(cv_ptr->toImageMsg());
        Mat imag=cv_ptr->image;
        leftObjectCenter_second.x = leftFaceRectangle.center.x;
        leftObjectCenter_second.y = leftFaceRectangle.center.y;
      leftPointPub_second.publish(leftObjectCenter_second); //发布得到的目标坐标点

        cv::imshow(OPENCV_WINDOW_left_second, imag);
        cv::waitKey(3);
    }
  }
  //OpenCV的边缘检测程序
  RotatedRect detect_edges_second(cv::Mat img)
  {
RotatedRect faceRectangle = RotatedRect(Point2i(0, 0), Size2i(0, 0), 0);//旋转矩形类(矩形中心 边长 旋转角度)
    cv::Mat src, src_gray;
    cv::Mat dst, detected_edges,OtsuImage;
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    int edgeThresh = 1;
    int lowThreshold = 200;
    int highThreshold =300;
    int kernel_size = 5;

    int largest_area = 0;
    int largest_contour_index = 0;
    Mat1b mask1, mask2;
    img.copyTo(src);
    Mat img2,mask;
    img.copyTo(src);
    cv::cvtColor( img, src_gray, cv::COLOR_BGR2GRAY );
    cv::blur( src_gray, detected_edges, cv::Size(5,5) );
  
    threshold(detected_edges,OtsuImage,200,255,THRESH_OTSU);//0不起作用,可为任意阈值
    cv::findContours( OtsuImage, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
    vector<Moments> mu(contours.size() );
\
        for( int i = 0; i < contours.size(); i++ )
        {
           // double a = contourArea(contours[i], false);  //  Find the area of contour
          //  if (a > largest_area) {
             //   largest_area = a;
            //    largest_contour_index = i;                //Store the index of largest contour
          //  }
           // mu[i] = moments( contours[i], false );
            mu[i] = moments( contours[i], false );
        }
        //计算轮廓的质心

        vector<Point2f> mc( contours.size() );

        for( int i = 0; i < contours.size(); i++ )
        {
            mc[i] = cv::Point2d( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 );
        }
       //画轮廓及其质心并显示
        cv::Mat drawing =cv::Mat::zeros( detected_edges.size(), CV_8UC3 );

            for( int i = 0; i< contours.size(); i++ )
            {
            Scalar color = Scalar( 255, 0, 0);
            drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );
            circle( drawing, mc[i], 5, Scalar( 0, 0, 255), -1, 8, 0 );
            rectangle(drawing, boundingRect(contours.at(i)), cvScalar(0,255,0));
            char tam[100];
            sprintf(tam, "(%0.0f,%0.0f)",mc[i].x,mc[i].y);
            putText(drawing, tam, Point(mc[i].x, mc[i].y), FONT_HERSHEY_SIMPLEX, 0.4, cvScalar(255,0,255),1);
}

    dst = cv::Scalar::all(0);
    img.copyTo( dst, detected_edges);
    drawing.copyTo(img);

        //cv::imshow(OPENCV_WINDOW, src);
        //cv::imshow(OPENCV_WINDOW_1, dst);
        //cv::imshow( "Contours", drawing );
      // cv::imshow( "dstImg", dstImg );
    if(contours.size()>0)

    {
        int i=0;
               for( int i = 0; i< contours.size(); i++ )
                {
               double a = contourArea(contours[i], false);  //  Find the area of contour
                           if (a > largest_area) {
                               largest_area = a;
                              largest_contour_index = i;                //Store the index of largest contour
                         }
               }
    faceRectangle.center.x = mc[largest_contour_index].x ;
            faceRectangle.center.y = mc[largest_contour_index].y;
            faceRectangle.size.width = 0;
            faceRectangle.size.height =0;
            faceRectangle.angle = 0;
            return faceRectangle;
    }

       // cv::waitKey(3);

  }

};


int main(int argc, char** argv)
{
  ros::init(argc, argv, "Edge_Detector_second");
  Edge_Detector_second ic;
  ros::spin();
  return 0;
}

然后是根据发布的两个坐标计算深度坐标


#include <ros/ros.h>
#include <geometry_msgs/Point.h> //点 消息类型
#include <geometry_msgs/PointStamped.h> 
#include <iostream>
#include <stdio.h>


#define SUBSCRIBE_FREQUENCY 5 
#define CAMERA_MODEL_FX 538.4 
#define DISTANCE_BETWEEN_CAMERAS 0.062 
#define IMAGE_WIDTH 640 
#define MODEL_PARAMETER (CAMERA_MODEL_FX * DISTANCE_BETWEEN_CAMERAS)
using namespace std;

ros::Publisher target3DPositionPub_second;
geometry_msgs::Point left2DPoint_second, right2DPoint_second;
geometry_msgs::PointStamped target3DPosition_second; 

void leftPointCallback(const geometry_msgs::Point& leftPoint_second)
{
  left2DPoint_second = leftPoint_second;
}

void rightPointCallback(const geometry_msgs::Point& rightPoint_second)
{
  right2DPoint_second = rightPoint_second;
}

//Z = fx*T / (leftpoin - rightpoint) y坐标的值

void CalTargetPos(geometry_msgs::Point& leftPoint, geometry_msgs::Point& rightPoint)
{
  target3DPosition_second.header.frame_id = "/map";
  target3DPosition_second.header.stamp = ros::Time::now();
  if(leftPoint.x ==0 || rightPoint.x == 0 || leftPoint.x == rightPoint.x)
  {
     target3DPosition_second.point.x = 0;
     target3DPosition_second.point.y = 0;
     target3DPosition_second.point.z = 0;
  }
  else 
  {
     target3DPosition_second.point.x = DISTANCE_BETWEEN_CAMERAS*(leftPoint.x+rightPoint.x-IMAGE_WIDTH)/2/(leftPoint.x-rightPoint.x);
     target3DPosition_second.point.y = MODEL_PARAMETER / (leftPoint.x - rightPoint.x);
    target3DPosition_second.point.z = DISTANCE_BETWEEN_CAMERAS*CAMERA_MODEL_FX/(leftPoint.y - rightPoint.y);
  }
  target3DPositionPub_second.publish(target3DPosition_second);
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "cal_target_3Dposition_second"); 
  ros::NodeHandle nh; 
  ros::Rate r(SUBSCRIBE_FREQUENCY); 
  target3DPositionPub_second = nh.advertise<geometry_msgs::PointStamped>("target_3Dposition_second", 1); 
  ros::Subscriber left2DPositionSub = nh.subscribe("/edge_detector/left_2Dposition_second", 1, leftPointCallback); 
  ros::Subscriber right2DPositionSub = nh.subscribe("/edge_detector/right_2Dposition_second", 1, rightPointCallback); 
  while(ros::ok())
  {
    CalTargetPos(left2DPoint_second, right2DPoint_second);
    ros::spinOnce(); 
    r.sleep();
  }

  return 0;
}

计算深度的话 只计算了识别到的最大的物体。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ROS中用opencv库实现物体中心点提取 的相关文章

  • 卡尔曼滤波(联邦、一致性卡尔曼滤波)

    在信息融合中经常使用卡尔曼滤波 xff0c 现在我们对其进行讲解 xff1a 百度百科上写到 xff1a 卡尔曼滤波公式如上 这是另一种表述 xff0c 涉及的符号见下表 xff1a 对于联邦卡尔曼滤波 xff1a 对于一致性卡尔曼滤波 x
  • BP神经网络公式推导(西瓜书)自我理解

    公式推导 描述BP神经网络 xff1a 网络包含三层 xff1a 输入层 xff1a 神经元数量为 d d d 个 xff0c 输入为 x i
  • C++ 友元类

    1 在C 43 43 中 xff0c 我们使用类对数据进行了隐藏和封装 xff0c 类的数据成员一般都定义为私有成员 xff0c 成员函数一般都定义为公有的 xff0c 以此提供类与外界的通讯接口 但是 xff0c 有时需要定义一些函数 x
  • 浪潮服务器通过BMC安装银河麒麟OS记录

    浪潮服务器 xff08 X86 xff09 远程安装银河kylin操作系统记录 1 下载麒麟镜像OS 官网下载 xff1a 银河麒麟官网 xff0c 按需申请即可这个是首页 xff0c 不要走错哦 2 通过网络登录BMC 浪潮服务器的默BM
  • K8S 性能优化 - 大型集群 CIDR 配置

    前言 K8S 性能优化系列文章 xff0c 本文为第三篇 xff1a Kubernetes 大型集群 CIDR 配置最佳实践 系列文章 xff1a K8S 性能优化 OS sysctl 调优 K8S 性能优化 K8S APIServer 调
  • 完美实现Ubuntu系统迁移到另一台电脑/服务器

    一 以A电脑的系统向B电脑迁移为例 第一 xff0c 首先进入A电脑根目录并获取权限 命令 xff1a cd sudo su 第二 xff0c 将根目录所需文件打包为backup tar gz放在当前目录下 xff0c 也可以修改路径直接保
  • catkin 创建工作区

    先确定自己的环境变量是否设置正确 export grep ROS 若出现如下的 xff0c 说明是正确的 declare x ROSLISP PACKAGE DIRECTORIES 61 declare x ROS DISTRO 61 in
  • semtcl-信号量的操作

    头文件 include lt sys types h gt include lt sys ipc h gt include lt sys sem h gt 函数 int semctl xff08 int semid xff0c int se
  • 计算机专业保研面试复习笔记——操作系统

    计算机专业保研面试复习笔记 xff1a 计算机专业保研面试复习笔记 数据结构中的重要算法 计算机专业保研面试复习笔记 数据库 计算机专业保研面试复习笔记 操作系统 计算机专业保研面试复习笔记 计算机网络 文章目录 死锁定义原因条件处理方法死
  • 计算机专业保研面试复习笔记——计算机网络

    计算机专业保研面试复习笔记 xff1a 计算机专业保研面试复习笔记 数据结构中的重要算法 计算机专业保研面试复习笔记 数据库 计算机专业保研面试复习笔记 操作系统 计算机专业保研面试复习笔记 计算机网络 文章目录 三种协议五层协议应用层 x
  • 北航计算机学院往年夏令营+考研面试数理题目汇总

    北航计算机学院硕士复试机经 43 面经 xff1a 北航计算机学院往年夏令营 43 预推免机试题目汇总 北航计算机学院往年夏令营 43 考研面试题目汇总 北航计算机学院往年夏令营 43 考研面试数理题目汇总 以下是笔者汇总的北航计算机学院往
  • 【已解决】nvidia-smi报错:NVIDIA-SMI has failed because it couldn’t communicate with the ... 阿里云GPU服务器

    问题描述 如题 xff0c 起因是在阿里云GPU服务器上 xff0c 使用原先正常运行的镜像生成了容器 xff0c 但容器的显卡驱动出问题了 xff0c 使用nvidia smi命令会报错 NVIDIA SMI has failed bec
  • 【ROS】wiki教程总结以及offboard仿真实现过程

    文章目录 概览ROS基本概念文件结构框架节点 node 之间通讯使用图形工具rqt plot xff1a 显示主题上发布的数据的滚动时间图rqt graph创建系统中正在节点间的动态图 xff1a 从头创建一个offboard包 xff0c
  • 光场相机系列-----相机标定

    光场相机系列 相机标定 通过标定相机 xff0c 得到相机的内参与外参 文章目录 光场相机系列 相机标定前言一 四种坐标系二 标定流程1 坐标系关系2 相机畸变 前言 能过相机标定 xff0c 计算出相机的内参与外参 xff0c 然后通过内
  • ubuntu安装CobaltStrike,MSF,john

    ubuntu安装CobaltStrike ubuntu做服务端 xff0c windows10本机做客户端 xff0c win7做运行木 haha马的机子 span class token function sudo span teamse
  • Android打开系统相册的问题

    xfeff xfeff 在Google Nexus 7 xff08 Version 4 4 2 xff09 平板出现之前 xff0c Intent ACTION GET CONTENT打开相册会返回如下形式的Uri content medi
  • 复试项目2 直流可变稳压电源

    项目描述 xff1a 用集成芯片制作一个0 15V的直流电源 xff0c 且该直流电源具有过压过流保护能力 责任描述 xff1a 负责查找资料 xff0c 选择相应芯片 xff0c 然后再Multisim仿真软件上进行仿真 待仿真电路完善能
  • ARM STMFD, STMFA, STMED, STMEA, LDMFD, LDMFA, LDMED, LDMEA

    LDM是出栈指令 xff0c STM是入栈指令 ED表示empty descending空递减 FA表示full ascending满递增 同理可知其它组合 STMED表示空递减入栈 xff0c 相当于STMDA指令 xff0c 即decr
  • 物理机可ping通虚拟机,虚拟机无法ping通物理机

    本次环境中 xff0c 虚拟机设置虚拟网卡ip 为172 26 130 X xff08 桥接模式 xff09 xff0c 物理机网卡ip为172 26 130 X xff0c 但实际设置后ipconfig 显示出来的ip为 169 254
  • iPhone使用过程中提示:“无线局域网似乎未接入互联网”,点击继续使用可正常上网

    小伙伴们好呀 最近小锐在处理问题时经常收到客户反馈说 xff1a 我的iPhone怎么用着用着偶尔就会出现弹窗提示 无线局域网似乎未接入互联网 xff0c 这个是怎么回事呀 xff1f 是不是无线网络出问题了啊 xff1f 咦 xff0c

随机推荐