设为首页收藏本站

ROS中国

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 活动 交友 discuz
查看: 4217|回复: 17

怎样用ROS和OpenCV做图像识别

[复制链接]
发表于 2015-3-2 05:52:16 | 显示全部楼层 |阅读模式
  • 软件和硬件环境:               
        1. Ubuntu 14.04
        2. ROS Indigo Desktop Full
        3. Logitech 1080p camera
        
  • 安装相机驱动
  1.  首先安裝ROS, 安裝教程
        2. 打开一个terminal, 安装uvc_camera
  1. $ sudo apt-get install ros-indigo-camera-umd
复制代码
3. 确认安装成功:
  1. $ roscd uvc_camera
复制代码
Note: Indigo有了专门的usb camera package, 如果是其他的相机,那么其他相应的driver,请点击查看  


  • 找到和电脑连接好的网络摄像头
       1.把USB摄像头和电脑连接,然后在terminal里面运行:
  1. $ ls /dev/video*
复制代码
然后会看到类似于下面的显示:
  1. /dev/video0
复制代码
如果只有一个摄像头相连,那么这个设备都会显示为video0

  • 建立一个package调用OpenCV的函数
  1. $ roscreate-pkg testcamera image_transport roscpp std_msgs opencv2 cv_bridge uvc_camera
复制代码
这样就建立了一个ROS package叫做 testcamera
  1. $ roscd testcamera
  2. $ mkdir launch
  3. $ cd launch
  4. $ vim uvcCameraLaunch.launch
复制代码
然后把下面的code粘贴在uvcCameraLaunch.launch
  1. <!--xml-->
  2. <launch>
  3. <node ns="camera" pkg="uvc_camera" type="camera_node" name="uvc_camera" output="screen">
  4. <param name="width" type="int" value="640" />
  5. <param name="height" type="int" value="480" />
  6. <param name="fps" type="int" value="30" />
  7. <param name="frame" type="string" value="webcam" />
  8. <param name="device" type="string" value="/dev/video0" />
  9. </node>
  10. </launch>
复制代码
注意把 /dev/video0 改成相应的相机path 就是前面$ ls /dev/video* 的结果


     把下面的路径加入到 .bashrc
  1. export ROS_PACKAGE_PATH=~/testcamera:$ROS_PACKAGE_PATH
复制代码
记得 source bashrc使之生效
  1. $ rosmake testcamera
复制代码
  • 测试 launch script
  1. $ roslaunch testcamera uvcCameraLaunch.launch
复制代码
如果你想要看到published topics,就打开一个新的terminal:
  1. $ rostopic list
复制代码
然后查看topic: /camera/image raw
  1. $ rosrun image_view image_view image:=/camera/image_raw
复制代码
  • 写图像识别的测试code
打开一个新的terminal
  1. $ roscd testcamera/src
复制代码
建立一个main.cpp 然后把下面的code粘贴进去:
  1. #include <ros/ros.h>
  2. #include <image_transport/image_transport.h>
  3. #include <cv_bridge/cv_bridge.h>
  4. #include <sensor_msgs/image_encodings.h>
  5. #include <opencv2/imgproc/imgproc.hpp>
  6. #include <opencv2/highgui/highgui.hpp>
  7. namespace enc = sensor_msgs::image_encodings;
  8. static const char WINDOW[] = "Image Processed";
  9. image_transport::Publisher pub;
  10. void imageCallback(const sensor_msgs::ImageConstPtr& original_image)
  11. {
  12. cv_bridge::CvImagePtr cv_ptr;
  13. try
  14. {
  15. cv_ptr = cv_bridge::toCvCopy(original_image, enc::BGR8);
  16. }
  17. catch (cv_bridge::Exception& e)
  18. {
  19. ROS_ERROR("testcamera::main.cpp::cv_bridge exception: %s", e.what());
  20. return;
  21. }
  22. for(int i=0; i<cv_ptr->image.rows; i++)
  23. {
  24. for(int j=0; j<cv_ptr->image.cols; j++)
  25. {
  26. for(int k=0; k<cv_ptr->image.channels(); k++)
  27. {
  28. cv_ptr->image.data[i*cv_ptr->image.rows*4+j*3 + k] = \
  29. 255-cv_ptr->image.data[i*cv_ptr->image.rows*4+j*3 + k];
  30. }
  31. }
  32. }
  33. cv::imshow(WINDOW, cv_ptr->image);
  34. cv::waitKey(3);
  35. pub.publish(cv_ptr->toImageMsg());
  36. }
  37. int main(int argc, char **argv)
  38. {
  39. ros::init(argc, argv, "image_processor");
  40. ros::NodeHandle nh;
  41. image_transport::ImageTransport it(nh);
  42. cv::namedWindow(WINDOW, CV_WINDOW_AUTOSIZE);
  43. image_transport::Subscriber sub = it.subscribe("camera/image_raw", 1, imageCallback);
  44. cv::destroyWindow(WINDOW);
  45. pub = it.advertise("camera/image_processed", 1);
  46. ros::spin();
  47. ROS_INFO("testcamera::main.cpp::No error.");
  48. }
复制代码
接下来需要修改CMakeList.txt
  1. $ roscd testcamera
  2. $ vim CMakeList
复制代码
回复

使用道具 举报

 楼主| 发表于 2015-3-2 05:58:18 | 显示全部楼层
续。。。。
把下面这一行粘贴到  CMakeList.txt 里面
  1. rosbuild_add_executable(testcamera src/main.cpp)
复制代码
然后编译这个package
  1. $ rosmake testcamera
复制代码
确保roscore在运行,如果没有的话
  1. $ roscore &
复制代码
最后运行package
  1. $ rosrun testcamera testcamera
  2. $ rosrun image_view image_view image:=/camera/image_processed
复制代码
然后应该就会看到相应的stream ideo capture。把相应的OpenCV算法调用到main.cpp中
就可以做相应基于颜色和结构的图像识别
回复

使用道具 举报

发表于 2015-3-25 13:42:33 | 显示全部楼层
楼主你好,我初学,也想在ros上用opencv做图像处理,那我应该把相应的opencv算法调用到main.cpp那个代码的什么地方呢?
回复

使用道具 举报

 楼主| 发表于 2015-3-26 01:51:27 | 显示全部楼层
lylyly 发表于 2015-3-25 13:42
楼主你好,我初学,也想在ros上用opencv做图像处理,那我应该把相应的opencv算法调用到main.cpp那个代码的 ...
  1. //Includes all the headers necessary to use the most common public pieces of the ROS system.
  2. #include <ros/ros.h>
  3. //Use image_transport for publishing and subscribing to images in ROS
  4. #include <image_transport/image_transport.h>
  5. //Use cv_bridge to convert between ROS and OpenCV Image formats
  6. #include <cv_bridge/cv_bridge.h>
  7. //Include some useful constants for image encoding. Refer to: http://www.ros.org/doc/api/sensor_msgs/html/namespacesensor__msgs_1_1image__encodings.html for more info.
  8. #include <sensor_msgs/image_encodings.h>
  9. //Include headers for OpenCV Image processing
  10. #include <opencv2/imgproc/imgproc.hpp>
  11. //Include headers for OpenCV GUI handling
  12. #include <opencv2/highgui/highgui.hpp>

  13. //Store all constants for image encodings in the enc namespace to be used later.
  14. namespace enc = sensor_msgs::image_encodings;

  15. //Declare a string with the name of the window that we will create using OpenCV where processed images will be displayed.
  16. static const char WINDOW[] = "Image Processed";

  17. //Use method of ImageTransport to create image publisher
  18. image_transport::Publisher pub;

  19. //This function is called everytime a new image is published
  20. void imageCallback(const sensor_msgs::ImageConstPtr& original_image)
  21. {
  22.     //Convert from the ROS image message to a CvImage suitable for working with OpenCV for processing
  23.     cv_bridge::CvImagePtr cv_ptr;
  24.     try
  25.     {
  26.         //Always copy, returning a mutable CvImage
  27.         //OpenCV expects color images to use BGR channel order.
  28.         cv_ptr = cv_bridge::toCvCopy(original_image, enc::BGR8);
  29.     }
  30.     catch (cv_bridge::Exception& e)
  31.     {
  32.         //if there is an error during conversion, display it
  33.         ROS_ERROR("tutorialROSOpenCV::main.cpp::cv_bridge exception: %s", e.what());
  34.         return;
  35.     }

  36.     //Invert Image
  37.     //Go through all the rows
  38.     for(int i=0; i<cv_ptr->image.rows; i++)
  39.     {
  40.         //Go through all the columns
  41.         for(int j=0; j<cv_ptr->image.cols; j++)
  42.         {
  43.             //Go through all the channels (b, g, r)
  44.             for(int k=0; k<cv_ptr->image.channels(); k++)
  45.             {
  46.                 //Invert the image by subtracting image data from 255               
  47.                 cv_ptr->image.data[i*cv_ptr->image.rows*4+j*3 + k] = 255-cv_ptr->image.data[i*cv_ptr->image.rows*4+j*3 + k];
  48.             }
  49.         }
  50.     }
  51.      

  52.     //Display the image using OpenCV
  53.     cv::imshow(WINDOW, cv_ptr->image);
  54.     //Add some delay in miliseconds. The function only works if there is at least one HighGUI window created and the window is active. If there are several HighGUI windows, any of them can be active.
  55.     cv::waitKey(3);
  56.     /**
  57.     * The publish() function is how you send messages. The parameter
  58.     * is the message object. The type of this object must agree with the type
  59.     * given as a template parameter to the advertise<>() call, as was done
  60.     * in the constructor in main().
  61.     */
  62.     //Convert the CvImage to a ROS image message and publish it on the "camera/image_processed" topic.
  63.         pub.publish(cv_ptr->toImageMsg());
  64. }

  65. /**
  66. * This tutorial demonstrates simple image conversion between ROS image message and OpenCV formats and image processing
  67. */
  68. int main(int argc, char **argv)
  69. {
  70.     /**
  71.     * The ros::init() function needs to see argc and argv so that it can perform
  72.     * any ROS arguments and name remapping that were provided at the command line. For programmatic
  73.     * remappings you can use a different version of init() which takes remappings
  74.     * directly, but for most command-line programs, passing argc and argv is the easiest
  75.     * way to do it.  The third argument to init() is the name of the node. Node names must be unique in a running system.
  76.     * The name used here must be a base name, ie. it cannot have a / in it.
  77.     * You must call one of the versions of ros::init() before using any other
  78.     * part of the ROS system.
  79.     */
  80.         ros::init(argc, argv, "image_processor");
  81.     /**
  82.     * NodeHandle is the main access point to communications with the ROS system.
  83.     * The first NodeHandle constructed will fully initialize this node, and the last
  84.     * NodeHandle destructed will close down the node.
  85.     */
  86.         ros::NodeHandle nh;
  87.     //Create an ImageTransport instance, initializing it with our NodeHandle.
  88.         image_transport::ImageTransport it(nh);
  89.     //OpenCV HighGUI call to create a display window on start-up.
  90.     cv::namedWindow(WINDOW, CV_WINDOW_AUTOSIZE);
  91.     /**
  92.     * Subscribe to the "camera/image_raw" base topic. The actual ROS topic subscribed to depends on which transport is used.
  93.     * In the default case, "raw" transport, the topic is in fact "camera/image_raw" with type sensor_msgs/Image. ROS will call
  94.     * the "imageCallback" function whenever a new image arrives. The 2nd argument is the queue size.
  95.     * subscribe() returns an image_transport::Subscriber object, that you must hold on to until you want to unsubscribe.
  96.     * When the Subscriber object is destructed, it will automatically unsubscribe from the "camera/image_raw" base topic.
  97.     */
  98.         image_transport::Subscriber sub = it.subscribe("camera/image_raw", 1, imageCallback);
  99.     //OpenCV HighGUI call to destroy a display window on shut-down.
  100.     cv::destroyWindow(WINDOW);
  101.     /**
  102.     * The advertise() function is how you tell ROS that you want to
  103.     * publish on a given topic name. This invokes a call to the ROS
  104.     * master node, which keeps a registry of who is publishing and who
  105.     * is subscribing. After this advertise() call is made, the master
  106.     * node will notify anyone who is trying to subscribe to this topic name,
  107.     * and they will in turn negotiate a peer-to-peer connection with this
  108.     * node.  advertise() returns a Publisher object which allows you to
  109.     * publish messages on that topic through a call to publish().  Once
  110.     * all copies of the returned Publisher object are destroyed, the topic
  111.     * will be automatically unadvertised.
  112.     *
  113.     * The second parameter to advertise() is the size of the message queue
  114.     * used for publishing messages.  If messages are published more quickly
  115.     * than we can send them, the number here specifies how many messages to
  116.     * buffer up before throwing some away.
  117.     */
  118.         pub = it.advertise("camera/image_processed", 1);
  119.     /**
  120.     * In this application all user callbacks will be called from within the ros::spin() call.
  121.     * ros::spin() will not return until the node has been shutdown, either through a call
  122.     * to ros::shutdown() or a Ctrl-C.
  123.     */
  124.         ros::spin();
  125.     //ROS_INFO is the replacement for printf/cout.
  126.     ROS_INFO("tutorialROSOpenCV::main.cpp::No error.");

  127. }
复制代码
回复

使用道具 举报

 楼主| 发表于 2015-3-26 02:05:20 | 显示全部楼层
lylyly 发表于 2015-3-25 13:42
楼主你好,我初学,也想在ros上用opencv做图像处理,那我应该把相应的opencv算法调用到main.cpp那个代码的 ...

你好 上个回复是一个比较详细的带注释的main.cpp文件(不过注释都是英文,如果有问题,欢迎留言讨论) 按照前面的教程安装好ROS和camera之后 需要安装OpenCV 值得注意的几点说明:

1. #include <opencv2/imgproc/imgproc.hpp>以及 #include <opencv2/highgui/highgui.hpp> 要把相应的头文件include进来,这样你才能调用OpenCV的算法

2. 具体看回复的第27行,cv_bridge::CvImagePtr 是把ROS publisher的图像通过一个ROS OpenCV的bridge转换成OpenCV可以识别的图像形式,这一步非常关键 否则OpenCV是不能直接识别ROS publish的图像格式的

3. 第59行其实就是一个OpenCV函数cv::imshow的调用, 第61行也是OpenCV函数的调用


欢迎讨论
回复

使用道具 举报

发表于 2015-3-26 14:51:33 | 显示全部楼层
rosebudflyaway 发表于 2015-3-26 02:05
你好 上个回复是一个比较详细的带注释的main.cpp文件(不过注释都是英文,如果有问题,欢迎留言讨论) 按 ...

恩恩,代码看懂了,43-55行应该对图像进行处理,这几行没用opencv的语法,还想问一下,比如说,我现在是想用cvSmooth的处理图像,但是头两个参数不知道该怎么写,程序现在还没有调出来。不知道楼主知不知道啊。
回复

使用道具 举报

 楼主| 发表于 2015-3-27 02:07:21 | 显示全部楼层
lylyly 发表于 2015-3-26 14:51
恩恩,代码看懂了,43-55行应该对图像进行处理,这几行没用opencv的语法,还想问一下,比如说,我现在是 ...

我自己的经验一般会在ROS环境之外,调用OpenCV的函数,调试完了如果work了那么就slightly改一下code 调用那个bridge就可以了,我自己用过OpenCV的SIFT和SURF函数,你可以贴一下你的代码
回复

使用道具 举报

发表于 2015-3-30 15:50:36 | 显示全部楼层
你好,我在创建的package是依赖了opencv2,但是在catkin_make的时候出错:
CMake Error at /opt/ros/indigo/share/catkin/cmake/catkinConfig.cmake:75 (find_package):
  Could not find a package configuration file provided by "opencv2" with any
  of the following names:

    opencv2Config.cmake
    opencv2-config.cmake

  Add the installation prefix of "opencv3" to CMAKE_PREFIX_PATH or set
  "opencv2_DIR" to a directory containing one of the above files.  If
  "opencv2" provides a separate development package or SDK, be sure it has
  been installed.
这该怎样解决呢?另外,我是单独安装的opencv。请楼主帮忙!
回复

使用道具 举报

 楼主| 发表于 2015-3-31 05:53:39 | 显示全部楼层
  1. cmake_minimum_required(VERSION 2.8)
  2. project( YOUR PROJECT NAME )
  3. find_package( OpenCV REQUIRED )
  4. add_executable( YOUR EXECUTABLE   YOUR SOURCE CODE )
  5. target_link_libraries( DisplayImage ${OpenCV_LIBS} )
复制代码
回复

使用道具 举报

 楼主| 发表于 2015-3-31 05:57:49 | 显示全部楼层
gengyongxiang 发表于 2015-3-30 15:50
你好,我在创建的package是依赖了opencv2,但是在catkin_make的时候出错:
CMake Error at /opt/ros/indigo ...

OpenCV是需要单独安装,但是你需要在CMakeList里面指定OpenCV的路径,否则是找不到的,而opencv2Config.cmake是set up路径然后用于CMakeLists找到你的OpenCV安装在哪里, 另外一个解决思路看http://answers.ros.org/question/56686/opencv-cmake-error/

一定要告诉CMakeLists你的OpenCV装在哪里,因为OpenCV不是ROS自带的package,默认是找不到的
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|ROS中国论坛    

GMT+8, 2017-3-24 15:56 , Processed in 0.198089 second(s), 18 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表