摘要:ros下,利用realsense D435采集深度圖,並將其轉換成opencv的數據類型。
一. RGBD圖像采集
通過image_transport包,根據給定的采集速度從realsense D435發布的topic中訂閱深度圖的代碼。
void depth_Callback(const sensor_msgs::ImageConstPtr& depth_msg) { cv_bridge::CvImagePtr depth_ptr; try { //cv::imshow("depth_view", cv_bridge::toCvShare(depth_msg, sensor_msgs::image_encodings::TYPE_16UC1)->image); //depth_ptr = cv_bridge::toCvCopy(depth_msg, sensor_msgs::image_encodings::TYPE_16UC1); cv::imshow("depth_view", cv_bridge::toCvShare(depth_msg, sensor_msgs::image_encodings::TYPE_32FC1)->image); depth_ptr = cv_bridge::toCvCopy(depth_msg, sensor_msgs::image_encodings::TYPE_32FC1); cv::waitKey(1000); } catch (cv_bridge::Exception& e) { ROS_ERROR("Could not convert from '%s' to '32fc1'.", depth_msg->encoding.c_str()); } }
在main函數中,
image_transport::ImageTransport it(nh);
image_transport::Subscriber sub1 = it.subscribe("/camera/aligned_depth_to_color/image_raw", 1, depth_Callback);
如代碼所示,在ros中采集到的深度圖是ros自帶的數據類型sensor_msgs::image。如果要利用opencv對該深度圖像進行操作,則需要轉換成opencv的圖像數據類型Mat,這一步轉換操作通過cv_bridge完成。
在上述代碼中, sensor_msgs::image_encodings::TYPE_16UC1 表示將ros數據類型轉換cv數據類型的編碼方式。一般而言,深度圖的編碼方式都有8UC, 16UC1, 32FC1等三種。在我們的程序中,如何判斷選用哪種編碼方式呢?最好的方法就是,深度圖轉換成cv數據類型后將深度值print出來,以此選定最佳的編碼方式。
二. 讀取深度值
深度圖轉換成opencv的數據類型后,需要讀取其中的深度值。這里着重介紹opencv矩陣元素的操作方法。
opencv中矩陣元素值的讀取方法,轉自https://blog.csdn.net/u011028345/article/details/73185166
1, 利用at函數讀取
(1)單通道圖像讀取
Mat img1 = imread(filename,IMREAD_GRAYSCALE); for( size_t nrow = 0; nrow < img1.rows; nrow++) { for(size_t ncol = 0; ncol < img1.cols; ncol++) { uchar val = mat_CV_8UC1.at<uchar>(nrow,ncol); } }
(2)三通道圖像讀取
Mat img2 = imread(filename,IMREAD_COLOR); for( size_t nrow = 0; nrow < img2.rows; nrow++) { for(size_t ncol = 0; ncol < img2.cols; ncol++) { Vec3i bgr = mat_CV_8UC3.at<Vec3b>(nrow,ncol);//用Vec3b也行 cout << "("<<bgr.val[0]<<"," <<bgr.val[1]<<"," <<bgr.val[2]<<")"; } cout << endl; }
2, 使用指針讀取
for( size_t nrow = 0; nrow < img3.rows; nrow++) { uchar* data = img3.ptr<uchar>(nrow); for(size_t ncol = 0; ncol < img3.cols * img3.channels(); ncol++) { cout << int( data[ncol] ) ; } cout << endl; }
3, 使用迭代器
Mat img4 = imread(filename,IMREAD_GRAYSCALE); MatIterator_<uchar> it = img4.begin<uchar>(), it_end = img4.end<uchar>(); for(int cnt = 1; it != it_end; ++it) { cout << ( int(*it) ) ; if( (cnt++ % img4.cols) ==0 ) cout << endl; }
4, 使用矩陣元素的地址定位
Mat img5(rows, cols,CV_8U, Scalar(0)); for( size_t nrow = 0; nrow < img5.rows; nrow++) for(size_t ncol = 0; ncol < img5.cols; ncol++) { cout<<(int)(*(img5.data+img5.step[0]*nrow+img5.step[1]*ncol)); }
5, 補充:在使用 at 函數的情況下需要預先知道Mat變量中存儲的元素類型,如果類型不匹配就會出現讀錯誤。所以可以采用c++ boost庫中的BOOST_TYPEOF來獲取圖像的元素數據類型。
Mat img6 = imread(filename); typedef BOOST_TYPEOF(*img6.data) ElementType for( size_t nrow = 0; nrow < img1.rows; nrow++) { for(size_t ncol = 0; ncol < img1.cols; ncol++) { cout<<mat_CV_8UC1.at<ElementType>(nrow,ncol); } }
關於如何判斷opencv矩陣元素的數據類型,方法轉自https://www.jianshu.com/p/204f292937bb
cv::Mat 類的對象有一個成員函數 type()
用來返回矩陣元素的數據類型,返回值是 int
類型,不同的返回值代表不同的類型。OpenCV Reference Manual 中對 type()
的解釋如下所示:
Mat::type
C++: int Mat::type() const
The method returns a matrix element type. This is an identifier compatible with the CvMat type system, like CV_16SC3 or 16-bit signed 3-channel array, and so on.
以本例的深度圖為,
int pic_type = depth_ptr->image.type(); std::cout << "the element type of depth_pic is " << pic_type << std::endl;
從type()函數返回一個整數值,下一步就是查找返回值和具體類型之間的對應關系。
OpenCV中定義的圖像數據類型可以由以下公式給出,
CV_<bit-depth>{U|S|F}C(<number_of_channels>).
其中,U為無符號整數,S為有符號整數,F為浮點數。
注意事項:
so CV_8UC3
is an 8-bit unsigned integer matrix/image with 3 channels. Although it is most common that this means an RGB (or actually BGR) image, it does not mandate it. It simply means that there are three channels, and how you use them is up to you and your application.
OpenCV常用的6種數據類型縮寫
b = unsigned char, 8 bit [0~255]
w = unsigned short, 16 bit [0~65535]
s = short, 16 bit [-32768~32767]
i = int
f = float
d = double