技術分享 | 機器人工程師,都是如何進行影象處理的?

相信大家在影象處理在機器人領域中是很常見的一個功能。透過計算機視覺獲取周圍環境。從而讓機器人“看見”世界,並且實施監控。本文將會詳細介紹ROS中的cv_bridge功能包,並給出使用的例子。幫助大家快速上手在ROS中使用OpenCV進行影象處理。

1. 什麼是cv_bridge

顧名思義,cv_bridge是一個在ROS中,將ROS影象訊息和OpenCV影象連線起來的一個橋樑。如圖所示:

技術分享 | 機器人工程師,都是如何進行影象處理的?

在ROS中,影象訊息的傳遞形式是ROS本身的訊息型別sensor_msgs/Image傳遞。但是大多數使用者都希望使用OpenCV。因此,cv_bridge就提供了一個ROS和OpenCV之間的介面。

2. cv_bridge使用詳解(C++)

cv_bridge定義了一個包含OpenCV的影象,並且包含ROS頭的編碼。CvImage類包含了ROS的影象訊息 sensor_msgs/Image的格式,因此,我們可以之間使用並且轉換。CvImage類定義如下:

技術分享 | 機器人工程師,都是如何進行影象處理的?

而當我們在轉換影象的時候,有以下兩種情形:a) 直接複製一份ROS訊息資料,然後對複製的影象進行修改。修改的是複製後的資料。此時並不影響原影象。b) 安全共享ROS訊息擁有的資料,而不是複製。意味著當你修改影象時,會影響原影象。

因此,針對這個情況,cv_bridge提供了針對上述情況的兩種函式,如下圖所示:

技術分享 | 機器人工程師,都是如何進行影象處理的?

兩種型別的函式,傳入的引數均是儲存影象資訊矩陣的指標,以及一個影象編碼引數(8UC3,8位,3通道)。兩個函式不同之處在於:

a) toCvCopy函式將會從ROS訊息複製一個影象資料的副本,即使原影象和編碼完全一致,也可以自由修改返回的CvImage,不對原影象造成影響。但是會消耗更多的系統資源。b) toCvShare函式為了避免複製,返回的是cv::Mat型別且指向ROS影象訊息的資料。當原影象和編碼完全一致的時候,此時如果記憶體中有ROS影象訊息的副本,將不會釋放記憶體。如果編碼不匹配,則會新開闢一塊緩衝區去執行轉換操作。無論是哪種情況,都禁止去修改返回的CvImage,因為它可能與ROS影象訊息共享資料。

如果沒有指定影象編碼,轉換的目標影象將會和原影象的編碼保持一致。toCvShare函式將不會複製影象資料。影象的編碼可以是下面OpenCV的任意一種:

8UC[1-4]:8位無符號整數1-4通道,即對應OpenCV中的CV_8UC1、CV_8UC2、CV_8UC3、CV_8UC48SC[1-4]16UC[1-4]16SC[1-4] :16位符號整數1-4通道32SC[1-4]32FC[1-4]64FC[1-4]

對於流行的影象編碼來說,cv_bridge將根據需要選擇做出顏色或者深度轉換,如果想要使用次特性,指定以下影象編碼:

mono8:CV_8UC1,即8位單通道(灰度圖)mono16:CV_16UC1,即16位單通道(灰度圖)bgr8:CV_8UC3,即8位藍綠紅色序彩色圖rgb8:CV_8UC3,即8位紅綠藍色序彩色圖brga8:CV_8UC4,即帶有alpha通道的8位BGR彩色圖rcba8:CV_8UC4,即帶有alpha通道的8位RGB彩色圖

備註:mono8和bgr8是大多數OpenCV函式所使用的影象編碼。

轉換OpenCV影象到ROS影象訊息,使用的是toImageMsg()函式,其定義如下:

技術分享 | 機器人工程師,都是如何進行影象處理的?

3. 使用示例

該功能包下有兩個節點,一個是影象釋出節點,將OpenCV影象轉換為ROS訊息影象併發布,一個是影象訂閱節點,用於訂閱ROS釋出的影象,並且將ROS影象轉換為OpenCV影象,並在上面畫一個圓圈,然後透過OpenCV顯示。使用該功能包需要用到的依賴有

sensor_msgscv_bridgeroscppstd_msgsimage_transport

釋出者原始碼如下:

1。/* 2。* 建立一個ROS節點 3。* 將OpenCV影象轉換為ROS影象訊息併發布 4。* OpenCV版本為4。1。0 5。* 阿木實驗室-青石 6。

/ 7。

8。//ROS標頭檔案

9。#include

10。

11。//影象相關檔案

12。#include

13。#include

14。#include

15。#include

16。#include

17。

18。//C++標準標頭檔案

19。#include

20。

21。

22。int main(int argc, char*

argv)

23。{

24。 //初始化ROS節點

25。 ros::init(argc, argv, “image_converter_pub_node”);

26。 ros::NodeHandle n;

27。

28。 //建立影象釋出者物件

29。 image_transport::ImageTransport image(n);

30。 image_transport::Publisher image_pub = image。advertise(“/camera/image”,1);

31。

32。 //OpenCV模組

33。 //讀取原始影象,並將影象矩陣賦值給 src_image

34。 cv::Mat src_image = cv::imread(“/home/loren/ros_ws/src/ros_vision/src/image/background。png”,-1);

35。

36。 //判斷影象是否讀取成功

37。 if(src_image。empty())

38。 {

39。 std::cout << “圖片不存在,請確認圖片路徑是否正確!” << std::endl;

40。 return -1;

41。 }

42。 else

43。 {

44。 std::cout << “影象載入成功!” << std::endl;

45。 }

46。

47。 cv::waitKey(0); //等待使用者按鍵,結束程式

48。

49。 //使用cv_bridge轉換圖片

50。 //建立ROS影象物件,用於儲存從OpenCV轉換到ROS的影象

51。 sensor_msgs::ImagePtr image_msg = cv_bridge::CvImage(std_msgs::Header(), “bgr8”, src_image)。toImageMsg();

52。

53。 ros::Rate loop_rate(5); //5Hz

54。

55。 while(ros::ok())

56。 {

57。 //迴圈釋出影象

58。 image_pub。publish(image_msg);

59。 loop_rate。sleep(); //保證釋出頻率為5Hz

60。 }

61。

62。 return 0;

63。}

訂閱者原始碼如下:1。/* 2。* 建立一個ROS節點,它將訂閱ROS釋出的影象 3。* 並將ROS影象轉換為cv::Mat(即:OpenCV影象), 4。* 並在上面畫一個圓,然後透過OpenCV顯示 5。* OpenCV版本為4。1。0 6。* 阿木實驗室-青石 7。

/ 8。

9。//ROS標頭檔案

10。#include

11。

12。//影象相關檔案

13。#include

14。#include

15。#include

16。#include

17。#include

18。

19。//C++標準標頭檔案

20。#include

21。

22。

23。void ImageCallback(const sensor_msgs::ImageConstPtr& msg)

24。{

25。 //建立cv_brigde物件,用於儲存從ROS轉換成OpenCV的影象資料

26。 cv_bridge::CvImagePtr cv_ptr;

27。

28。 try

29。 {

30。 //這裡選擇toCvCopy函式,是為了在其副本上畫圖,而不影響原影象

31。 //影象編碼格式為 BGR8

32。 cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);

33。 }

34。 catch(cv_bridge::Exception& e)

35。 {

36。 ROS_ERROR(“cv_bridge exception: %s”, e。what());

37。 return;

38。 }

39。

40。 //在影象上畫圓

41。 //畫一個直徑為50的圓,故需要影象尺寸大於60

60

42。 if(cv_ptr->image。rows> 60

43。 && cv_ptr->image。cols > 60)

44。 {

45。 std::cout << “開始畫圓!” << std::endl;

46。 cv::circle(cv_ptr->image, cv::Point(50, 50), 10, CV_RGB(255, 0, 0));

47。 }

48。

49。

50。 //GUI視窗顯示影象

51。 cv::imshow(“image view”, cv_ptr->image);

52。 cv::waitKey(0); //等待使用者按鍵,結束程式

53。}

54。

55。int main(int argc, char** argv)

56。{

57。 //初始化ROS節點

58。 ros::init(argc, argv, “image_converter_sub_node”);

59。 ros::NodeHandle n;

60。

61。 //建立影象訂閱者物件

62。 image_transport::ImageTransport image(n);

63。 image_transport::Subscriber image_sub = image。subscribe(“/camera/image”, 1, ImageCallback);

64。 ros::spin(); //迴圈呼叫回撥函式,該行後面的程式碼均不會被執行

65。

66。 return 0; //不會被執行

67。}

相信經過本文的梳理和案例,可以給讀者提供一個如何使用cv_bridge功能包的思路。透過這個包,將ROS和OpenCV結合起來,讓ROS機器人的開發有了更多,更加寬廣的選擇。也讓機器人的世界,變得豐富多彩!

End -

技術發展的日新月異,阿木實驗室將緊跟技術的腳步,不斷把機器人行業最新的技術和硬體推薦給大家。看到經過我們培訓的學員在技術上突飛猛進,是我們培訓最大的價值。如果你在機器人行業,就請關注我們的公眾號,我們將持續釋出機器人行業最有價值的資訊和技術。

阿木實驗室致力於前沿IT科技的教育和智慧裝備,讓機器人研發更高效!