譯:ROS影象和OpenCV影象相互轉換
ROS影象和OpenCV影象相互轉換
描述: 本文將描述如何使用cv_bridge來將ROS影象轉換為OpenCV影象,以及OpenCV轉為ROS影象。
1. 概念
在ROS中是以自己的sensor_msgs/Image格式對影象進行處理的,但開發者可能會希望以OpenCV的格式對影象進行處理。CvBridge就是一個提供ROS和OpenCV間介面的ROS庫,它可以在vision_opencv Stack的cv_bridge包中找到。
在本文中,你將學習如何編寫一個利用CvBridge將ROS影象格式轉換為OpenCV中的cv::Mat格式。還將學習如何將OpenCV影象轉換為ROS格式以在ROS中釋出。
1.1 從C-Tutle或更早的版本程式碼遷移
關於OpenCV,在ROS Diamondback中有很大的api變化,雖然後向相容維護了一陣,但從hydro開始有些已經被移除了,如sensor_msgs/CvBridge。關於遷移的問題見連結。
2. 轉換ROS影象到OpenCV影象
CvBridge中定義了一個包含OpenCV影象及其編碼、ROS header的CvImage型別。CvImage中包含了sensor_msgs/Image中的所有資訊,因此我們可以在這兩者間相互轉換。CvImage的class如下:
namespace cv_bridge {
class CvImage
{
public:
std_msgs::Header header;
std::string encoding;
cv::Mat image;
};
typedef boost::shared_ptr<CvImage> CvImagePtr;
typedef boost::shared_ptr<CvImage const> CvImageConstPtr;
}
當從sensor_msgs/Image訊息轉換到CvImage時,CvBridge認為有兩種不同的用例:
- 我們想修改資料,需要拷貝一份資料。
- 我們不想修改資料,但需要使用CvImage格式,所以可以通過const指標共享該資料。
CvBridge中提供了以下函式來轉換到CvImage:
// Case 1: Always copy, returning a mutable CvImage
CvImagePtr toCvCopy(const sensor_msgs::ImageConstPtr& source,
const std::string& encoding = std::string());
CvImagePtr toCvCopy(const sensor_msgs::Image& source,
const std::string& encoding = std::string());
// Case 2: Share if possible, returning a const CvImage
CvImageConstPtr toCvShare(const sensor_msgs::ImageConstPtr& source,
const std::string& encoding = std::string());
CvImageConstPtr toCvShare(const sensor_msgs::Image& source,
const boost::shared_ptr<void const>& tracked_object,
const std::string& encoding = std::string());
函式的輸入是一個影象指標,以及一個可選的編碼引數用於規定目標CvImage的編碼。
toCvCopy建立一個影象資料的拷貝,你可以對返回的CvImage進行修改。
toCvShare將會返回一個指向ROS訊息的cv::Mat const指標防止修改,只要你有返回的CvImage指標的拷貝,ROS訊息就不會被釋放。如果編碼不匹配,ROS將分配一個新的buffer並執行轉換,但你還是不能對其進行修改。
注:當你有一個包含sensor_msgs/Image的其他訊息型別的指標時,使用toCvShare的第二種過載方法將更為方便。
如果沒有給定編碼資訊,目標影象的編碼將與源影象一樣,在這種情況下toCvShare能保證不會對資料進行拷貝。影象編碼可以是一下任意一種OpenCV支援的影象編碼:
- 8UC[1-4]
- 8SC[1-4]
- 16UC[1-4]
- 16SC[1-4]
- 32SC[1-4]
- 32FC[1-4]
- 64FC[1-4]
對於某些常用的編碼,CvBridge提供了可選的color或pixel depth的轉換,要想使用這個特性,需要指定以下編碼格式:
- mono8: CV_8UC1, grayscale image
- mono16: CV_16UC1, 16-bit grayscale image
- bgr8: CV_8UC3, color image with blue-green-red color order
- rgb8: CV_8UC3, color image with red-green-blue color order
- bgra8: CV_8UC4, BGR color image with an alpha channel
- rgba8: CV_8UC4, RGB color image with an alpha channel
其中mono8和bgr8是大多數OpenCV函式所期望的影象編碼格式。
最後,CvBridge也可以識別OpenCV中8UC1型別的Bayer pattern編碼,CvBridge將不會對Bayer pattern進行轉換,一般是由image_proc進行轉換的。CvBridge可以識別一下以下集中Bayer編碼:
- bayer_rggb8
- bayer_bggr8
- bayer_gbrg8
- bayer_grbg8
3. 將OpenCV影象轉換為ROS影象訊息
要轉換CvImage為ROS影象訊息,可以使用toImageMsg()成員方法:
class CvImage
{
sensor_msgs::ImagePtr toImageMsg() const;
// Overload mainly intended for aggregate messages that contain
// a sensor_msgs::Image as a member.
void toImageMsg(sensor_msgs::Image& ros_image) const;
};
如果CvImage是你自己建立的,不要忘了填充header和編碼欄位。對於自己建立CvImage的例子,可以參考釋出影象教程
4. 一個ROS節點例子
這裡展示一個監聽ROS影象訊息話題的節點,並將該影象轉換為cv::Mat格式,然後使用OpenCV在影象上畫一個圓並進行顯示。最後該影象將在ROS中重新發布。
在你的package.xml和CMakeLists.xml(或者在你使用catkin_create_pkg時)新增一下依賴:
sensor_msgs
cv_bridge
roscpp
std_msgs
image_transport
在src資料夾中建立image_converter.cpp檔案,內容如下:
#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>
static const std::string OPENCV_WINDOW = "Image window";
class ImageConverter
{
ros::NodeHandle nh_;
image_transport::ImageTransport it_;
image_transport::Subscriber image_sub_;
image_transport::Publisher image_pub_;
public:
ImageConverter()
: it_(nh_)
{
// Subscribe to input video feed and publish output video feed
image_sub_ = it_.subscribe("/camera/image_raw", 1,
&ImageConverter::imageCb, this);
image_pub_ = it_.advertise("/image_converter/output_video", 1);
cv::namedWindow(OPENCV_WINDOW);
}
~ImageConverter()
{
cv::destroyWindow(OPENCV_WINDOW);
}
void imageCb(const sensor_msgs::ImageConstPtr& msg)
{
cv_bridge::CvImagePtr cv_ptr;
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 > 60 && cv_ptr->image.cols > 60)
cv::circle(cv_ptr->image, cv::Point(50, 50), 10, CV_RGB(255,0,0));
// Update GUI Window
cv::imshow(OPENCV_WINDOW, cv_ptr->image);
cv::waitKey(3);
// Output modified video stream
image_pub_.publish(cv_ptr->toImageMsg());
}
};
int main(int argc, char** argv)
{
ros::init(argc, argv, "image_converter");
ImageConverter ic;
ros::spin();
return 0;
}
對程式碼進行分解:
#include <image_transport/image_transport.h>
在ROS中使用image_transport釋出訂閱影象可以讓你訂閱壓縮後的影象流,記得在package.xml中include image_transport。
#include <cv_bridge/cv_bridge.h>
#include <sensor_msgs/image_encodings.h>
include CvBridge的標頭檔案以及image encodings(包含了很多有用的常量和函式),記得在package.xml中include cv_bridge。
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
includeOpenCV的影象處理和GUI模組標頭檔案,記得在package.xml中include opencv2。
ros::NodeHandle nh_;
image_transport::ImageTransport it_;
image_transport::Subscriber image_sub_;
image_transport::Publisher image_pub_;
public:
ImageConverter()
: it_(nh_)
{
// Subscrive to input video feed and publish output video feed
image_sub_ = it_.subscribe("/camera/image_raw", 1,
&ImageConverter::imageCb, this);
image_pub_ = it_.advertise("/image_converter/output_video", 1);
使用image_transport訂閱釋出影象話題。
cv::namedWindow(OPENCV_WINDOW);
}
~ImageConverter()
{
cv::destroyWindow(OPENCV_WINDOW);
}
在初始化和析構時呼叫OpenCV HighGUI來建立及銷燬視窗。
void imageCb(const sensor_msgs::ImageConstPtr& msg)
{
cv_bridge::CvImagePtr cv_ptr;
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;
}
在我們的回撥函式中,首先將ROS影象訊息轉換為了CvImage以在OpenCV中使用。因為我們需要在影象中畫圓,所以需要一個影象的拷貝,應使用toCvCopy()。sensor_msgs::image_encodings::BGR8是”bgr8”字串常量。
呼叫toCvCopy()/toCvShared()時需要捕獲異常,因為這些函式不會校驗資料的有效性。
// Draw an example circle on the video stream
if (cv_ptr->image.rows > 60 && cv_ptr->image.cols > 60)
cv::circle(cv_ptr->image, cv::Point(50, 50), 10, CV_RGB(255,0,0));
// Update GUI Window
cv::imshow(OPENCV_WINDOW, cv_ptr->image);
cv::waitKey(3);
在影象中畫一個紅色的圓圈並進行顯示。
// Output modified video stream
image_pub_.publish(cv_ptr->toImageMsg());
將CvImage轉為ROS影象訊息並進行釋出。
執行這個節點你需要一個影象流,啟動一個攝像頭或者回放一個bag檔案來生成影象流。現在你可以將”in”topic重新對映remaaping到真實的影象流話題。
如果你成功地轉換為OpenCV影象,你將在建立的視窗中看到新增圓圈之後的影象。
你可以通過rostopic或image_view檢視影象來確認節點是否正確地釋出了影象。
5. 共享影象資料的例子
在上節中我們建立了影象的拷貝,但共享影象也很容易:
namespace enc = sensor_msgs::image_encodings;
void imageCb(const sensor_msgs::ImageConstPtr& msg)
{
cv_bridge::CvImageConstPtr cv_ptr;
try
{
cv_ptr = cv_bridge::toCvShare(msg, enc::BGR8);
}
catch (cv_bridge::Exception& e)
{
ROS_ERROR("cv_bridge exception: %s", e.what());
return;
}
// Process cv_ptr->image using OpenCV
}
如果輸入影象的編碼是”bgr8”,cv_ptr將會是影象資料的一個別名而非拷貝。如果輸入影象不是”bgr8”編碼但是可轉換為”bgr8”編碼(如”mono8”),CvBridge將會為cv_ptr分配一個新的buffer並執行轉換。如果沒有異常捕獲語句的話一行程式碼就能共享影象了,但可能輸入影象的編碼無法轉換為目標編碼從而導致節點崩潰。例如,當輸入影象是從一個Bayer pattern攝像機的image_raw話題接收的,CvBridge將會丟擲異常,因為不支援Bayer到color的自動轉換。
一個稍微更復雜的例子:
namespace enc = sensor_msgs::image_encodings;
void imageCb(const sensor_msgs::ImageConstPtr& msg)
{
cv_bridge::CvImageConstPtr cv_ptr;
try
{
if (enc::isColor(msg->encoding))
cv_ptr = cv_bridge::toCvShare(msg, enc::BGR8);
else
cv_ptr = cv_bridge::toCvShare(msg, enc::MONO8);
}
catch (cv_bridge::Exception& e)
{
ROS_ERROR("cv_bridge exception: %s", e.what());
return;
}
// Process cv_ptr->image using OpenCV
}
在這個例子中,如果可以的話我們將使用color的編碼,不行的話就是用monochrome型別的編碼,如果輸入影象是”bgr8”或”mono8”編碼,將不會進行拷貝。
相關推薦
譯:ROS影象和OpenCV影象相互轉換
ROS影象和OpenCV影象相互轉換 描述: 本文將描述如何使用cv_bridge來將ROS影象轉換為OpenCV影象,以及OpenCV轉為ROS影象。 1. 概念 在ROS中是以自己的sensor_msgs/Image格式對影象進行處理的,但開發者可
ROS影象和OpenCV影象之間的轉換(C ++)
1.概念 ROS以自己的sensor_msgs / Image訊息格式傳遞影象,但許多使用者希望將影象與OpenCV結合使用。 CvBridge是一個ROS庫,提供ROS和OpenCV之間的介面。 在本教程中,您將學習如何編寫使用CvBridge將ROS影象轉換為O
經得起雷劈:關於double和int/long相互轉換失去精度計算錯誤的問題
http://blog.csdn.net/vonger/article/details/6656219 在用C/C++做運算時,型別轉換是很常見的,一般都直接用資料型別進行預設強制轉化,但是這樣其實是有很大問題的。 #include <stdio.h&g
譯:ROS和Openv之間的影象轉換(Python)
描述:本教程介紹如何使用cv_bridge將ROS影象轉換為OpenCV影象,反之亦然,從而實現ROS和OpenCV的互動。 包括一個示例節點,可用作您自己節點的模板。1.概念 ROS以其自己的sensor_msgs/Image訊息格式釋出影象,但許多使用者會希望將影象
Ros影象與Opencv影象的相互轉換(C++)
#include <ros/ros.h> #include <image_transport/image_transport.h> #include <cv_bridge/cv_bridge.h> #include <sensor_msgs/image_e
ROS影象與OpenCV影象格式轉換(C++)
一、ROS官網Converting between ROS images and OpenCV images (C++) 二、使用cv_bridge遇到的問題 1、標頭檔案 #include <opencv2/imgproc/imgproc.hpp> #includ
Python-opencv 筆記8 -- PIL.Image和OpenCV影象格式轉換
Python-opencv 筆記8 – PIL.Image和OpenCV影象格式轉換 1、PIL.Image 轉 OpenCV import cv2 from PIL import Image imp
ROS使用openni獲取Kinect彩色影象和深度影象
本實驗使用Ubuntu14.04的64bit版本,ROS使用Indigo版本,影象獲取使用OpenNI1(因為OpenNI2中未找到彩色影象和深度影象對齊功能,臺灣的一代大神Heresy已經實現這一功能,但是我更喜歡OpenNI1官方對齊方法),影象處理使用OpenCV2
(攝像頭視訊處理)將ROS節點轉為opencv 影象----cv_bridge 順便顯示兩個相機
#include <ros/ros.h> #include<image_transport/image_transport.h> #include<cv_bridge/cv_bridge.h> #include<sens
C#實戰小技巧(九):List<string>和string[]的相互轉換
List是string型別列表,string[]是string型別陣列,二者可以互相轉換。 1.string[]轉List string[] strArray = {"a", "ab", "abc"}; List<string> strList = new List<s
線性代數之——行影象和列影象
1. 線性方程組的幾何解釋 線性代數的中心問題就是解決一個方程組,這些方程都是線性的,也就是未知數都是乘以一個數字的。 \[\begin{alignedat}{2} &x \space- \space&2&y \space=\space 1 \\ 3&x\space+\sp
MIT與谷歌專家合著論文:機器學習和神經科學的相互啟發與融合
摘 要 神經科學專注的點包括計算的細節實現,還有對神經編碼、力學以及迴路的研究。然而,在機器學習領域,人工神經網路則傾向於避免出現這些,而是往往使用簡單和相對統一的初始結構,以支援成本函式(cost funcion)的蠻力最優化。近期出現了兩項機器學習方面的進展,或許會將這兩種看似不同的
OpenCV: Mat與IplImage*間的相互轉換
1. Mat -->IplImage Mat mat_img=imread("samples.bmp"); IplImage* ipl_img; ipl_img = &IplImage(mat_img); 2. IplImage--->Mat IplImage*
Beginng_Java7(譯):發現類和物件(第二章3.4.5節)(完)
多型 一些現實世界的實體可以改變他們的形式。 例如,水(在地球上而不是星際空間)自然是液體,但在冷凍時會變成固體,在加熱到沸點時會變成氣體。 諸如經歷變態的蝴蝶之類的昆蟲是另一個例子。 改變形式的能力被稱為多型,並且對於在程式語言中建模是有用的。 例如,通過引入
由RGB影象和深度影象獲取點雲
#include <iostream> #include <fstream> #include <string> #include <pcl/io/pcd_io.h> #include <pcl/io/ply_io.h> #include
SOS, 請教:XML和struct之間相互轉換的問題
現在配置基本都像XML方向轉變。那麼, 1、以前是按struct大小直接寫入檔案的,反過來,就直接按struct大小讀入struct就可以了,簡潔; 2、轉XML後,涉及到struct到xml節點之間的相互轉換,這個非常多的重複程式碼。而且太繁瑣,舉個例子,假設一個配置struct有100個欄位,
Python程式設計:namedtuple命名元組和dict字典相互轉換
from collections import namedtuple dct = { "name": "Tom", "age": 24 } Person = namedtuple("
realsense對齊彩色影象和深度影象
首先宣告realsense通道,封裝實際裝置和感測器 //初始化 pipeline pipe; pipeline_profile profile = pipe.start(); rs2_stream align_to = find_stream_to_align(p
python+opencv計算程式碼執行時間:time庫和opencv自帶方法getTickCount
import cv2 import time ############################## 利用opencv的兩個函式進行時間耗費計算 # cv2.getTickCount()記錄當前
jquery操作iframe的方法:父頁面和子頁面相互操作的方法
今天在弄jquery操作iframe中元素:先由iframe中的子頁面b.html給外面的父頁面a.html頁面傳值,再將a.html頁面計算機的值放到b.html頁面上,這裡就用到子頁面和父頁面相互傳值,相互呼叫更自函式這些功能,這裡我用一個簡單的例子來介紹一下這些方法。