1. 程式人生 > >ROS學習筆記(2):在ROS中使用OpenCV進行簡單的影象處理---程式碼實現篇

ROS學習筆記(2):在ROS中使用OpenCV進行簡單的影象處理---程式碼實現篇

再上一篇blog中,筆者總結了ROS系統中使用OpenCV庫的進行簡單影象處理的原理、系統相關的設定和程式包的下載。在這篇部落格中,筆者將從程式碼層面介紹如何實現在ROS系統中讀取圖片,並使用OpenCV進行影象處理,在返回結果。

例項:從ROS中讀取圖象,轉換後將彩色圖象變成灰度圖象,並返回灰度圖象,轉換後在ROS下輸出。

正文

1. 在ROS下建立工作空間

工作空間(work space)是ROS中非常重要的一個概念,可以把工作空間理解為一個大的工廠,裡面的分成幾個大的生產車間(package),每一個生產車間中會有若干個具有不同技能的工人(node)。當工廠運轉時,每個車間中的工人(node)同時工作,他們通過話題(topic)進行資訊溝通。各個大的車間之間也存在這互相依賴的關係,共同組成一個有機的整體。

因此在每次編寫ROS下的程式時都應該先建立一個獨立的工作空間,然後再不段的豐滿它的功能。

方法如下:新建一個終端輸入:

    mkdir -p cv_ws/src

    cd src

    catkin_init_workspace

    cd ..

    catkin_make


2. 在工作空間下建立程式包

建立好了工作空間,下一步需要建立程式包。在ROS中節點是實現某一個功能的可執行檔案(工人),一個或者多個節點可以組成一個程式包。這樣做便於程式碼的複用。程式包預設在工作空間中的src資料夾中建立,而node預設在程式包的src檔案中建立(cpp檔案)。

方法如下:在原來的終端下繼續輸入:

    cd src

    catkin_create_pkg robot_vision roscpp std_msgs cv_bridge image_transport sensor_msgs

    cd ..

    catkin_make

建立程式包的一般格式是catkin_create_pkg <name> <dependencies package>,在本程式中我們要用到除roscpp之外的三個程式包進行圖片的轉換工作。

3. 建立.cpp原始檔

在ubuntu系統下,沒有類似vs2010那樣的整合開發平臺,編寫程式只需要在文字中編寫,命名時採用相應的字尾即可。在建立的程式包的src檔案中建立一個文字檔案,並命名為getImage.cpp。具體程式碼和註釋如下:

#include<ros/ros.h> //ros標準庫標頭檔案
#include<iostream> //C++標準輸入輸出庫
/*
  cv_bridge中包含CvBridge庫
*/
#include<cv_bridge/cv_bridge.h> 
/*
  ROS圖象型別的編碼函式
*/
#include<sensor_msgs/image_encodings.h> 
/*
   image_transport 標頭檔案用來在ROS系統中的話題上釋出和訂閱圖象訊息
*/
#include<image_transport/image_transport.h> 

//OpenCV2標準標頭檔案
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>

static const std::string INPUT = "Input"; //定義輸入視窗名稱
static const std::string OUTPUT = "Output"; //定義輸出視窗名稱

//定義一個轉換的類
class RGB_GRAY
{
private:
    ros::NodeHandle nh_; //定義ROS控制代碼
    image_transport::ImageTransport it_; //定義一個image_transport例項
    image_transport::Subscriber image_sub_; //定義ROS圖象接收器
    //image_transport::Publisher image_pub_; //定義ROS圖象釋出器
public:
    RGB_GRAY()
      :it_(nh_) //建構函式
    {
        image_sub_ = it_.subscribe("camera/rgb/image_raw", 1, &RGB_GRAY::convert_callback, this); //定義圖象接受器,訂閱話題是“camera/rgb/image_raw”
       // image_pub_ = it_.publishe("", 1); //定義圖象釋出器
        //初始化輸入輸出視窗
        cv::namedWindow(INPUT);
        cv::namedWindow(OUTPUT);
    }
    ~RGB_GRAY() //解構函式
    {
         cv::destroyWindow(INPUT);
         cv::destroyWindow(OUTPUT);
    }
    /*
      這是一個ROS和OpenCV的格式轉換回調函式,將圖象格式從sensor_msgs/Image  --->  cv::Mat
    */
    void convert_callback(const sensor_msgs::ImageConstPtr& msg) 
    {
        cv_bridge::CvImagePtr cv_ptr; // 宣告一個CvImage指標的例項

        try
        {
            cv_ptr =  cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::RGB8); //將ROS訊息中的圖象資訊提取,生成新cv型別的圖象,複製給CvImage指標
        }
        catch(cv_bridge::Exception& e)  //異常處理
        {
            ROS_ERROR("cv_bridge exception: %s", e.what());
            return;
        }

        image_process(cv_ptr->image); //得到了cv::Mat型別的圖象,在CvImage指標的image中,將結果傳送給處理函式   
    }
    /*
       這是圖象處理的主要函式,一般會把影象處理的主要程式寫在這個函式中。這裡的例子只是一個彩色圖象到灰度圖象的轉化
    */
    void image_process(cv::Mat img) 
    {
       cv::Mat img_out;
       cv::cvtColor(img, img_out, CV_RGB2GRAY);  //轉換成灰度圖象
       cv::imshow(INPUT, img);
       cv::imshow(OUTPUT, img_out);
       cv::waitKey(5);
    }
};

//主函式
int main(int argc, char** argv)
{
    ros::init(argc, argv, "RGB");
    RGB_GRAY obj;
    ros::spin();
}

4. 以下是對上面程式碼中核心的部分進行解釋

1)標頭檔案

#include<cv_bridge/cv_bridge.h>
標頭檔案cv_bridge中包含了CvBridge類,而CvBridge中的API可以將ROS下的sensor_msgs/Image訊息型別轉化成cv::Mat。
#include<sensor_msgs/image_encodings.h>
標頭檔案sensor_msgs/Image是ROS下的影象的型別,這個標頭檔案中包含對影象進行編碼的函式。
#include<image_transport/image_transport.h>
這個標頭檔案中包含的是ImageTransport類,這個類提供ROS中影象的訂閱和釋出。 剩下的標頭檔案就是如程式中的註釋所示。

2)核心類的主要API

image_transport類:影象傳輸類,其功能和ROS中的Publisher和Subscriber差不多,但是不同的是這個類在釋出和訂閱圖片訊息的同時還附帶這攝像頭的資訊。相比較之下,   在ROS中傳送圖片資訊,使用image_transport類要高效的多。同時,這個類可以以不同的影象壓縮形式傳輸影象例如JPG/PNG等等,也可以新增外掛定義傳輸資料的壓縮模式。                                      API:                                      image_transport::ImageTransport類:這個類成員可以定義某個話題的影象型別的釋出器和訂閱器(類似ros的控制代碼)。                                      image_transport::Publisher類:這個類定義了image_transport中的釋出器。                                      image_transport::Subsciber類:這個類定義了image_transport中的訂閱器。 cv_bridge類:這個類中提供的API主要功能是將影象從sensor_msgs/Image型別轉化成cv::Mat型別。                           API:                           cv_bidge::CvImage類:cv_bridge中提供的資料結構,裡面包括OpenCV中的cv::Mat型別的影象資訊,影象編碼方式,ROS標頭檔案等等。要得到cv::Mat型別的 影象,只需要定義一個物件然後給出物件中的成員object.image即可,或者指標CvImagePtr,ptr->image。                           cv_bridge::toCvCopy()方法:引數是ROS下的sensor_msgs/ImageConstPtr,和影象壓縮型別(例如:sensor_msgs::image_encodings::RGB8)。其功能是實 現複製影象資訊這樣,得到副本,這樣我們可以從副本的CvImage中提取cv::Mat型別的影象進行處理。核心!                           cv_bridge::toCvShare()方法:引數同上,但是這個函式只是共享指標地址。ROS是不予許直接對影象格式的訊息進行操作的。

5. 編譯成可執行檔案

在編寫程式後,這個文字程式在編譯成可執行檔案之前是不能夠執行的。首先在建立的robot_vision的程式包中的CMakeLists.txt檔案中加入如下程式碼:
add_executable(gratImage src/grayImage.cpp)   //將src中的檔案新增成名字為grayImage的可執行檔案
target_link_libraries(grayImage ${catkin_LIBRARIES})  //將相關的庫和可執行檔案連結
add_dependencies(grayImage robot_vision_generate_messages_cpp)  //給可執行檔案新增依賴包
返回到工作空間下編譯。catkin_make                                               

6. 總結

以上就是在ROS下使用opencv所要做的所有工作。總體上來看就是因為opencv處理的影象格式和ROS系統實際提供的格式不匹配,故而有cv_bridge這個包作為溝通的橋樑。二本程式中使用image_transport代替傳統的ROS中的Publisher和Subscriber是另一個突破。同時還要注意的是,ubuntu下的opencv2中的某些函式的形式和window中的不相同,使用的時候要注意。 注意:程式設計的時候註釋儘量使用英文,這樣就不會造成程式碼中出現中文字元而出現莫名其妙的編譯錯誤。