How to display CV_32FC2 images in ROS
I'm trying to use OpenCV to calculate real-rime optical flow and display it in ROS. The task, using provided OpenCV's functionality, seems fairly easy. I've used uvc_camera
and cv_bridge
to capture images with ROS and convert them to cv::Mat
. Then, I'm using calcOpticalFlowFarneback
to calculate optical flow, which returns a cv::Mat
optical flow image of type CV_32FC2.
The code I'm using to publish the image looks like this:
cv_bridge::CvImage flowImage;
sensor_msgs::ImagePtr flowImagePtr;
if (!_flow.empty()) {
flowImage.header = cvImagePtr_->header; //copy header from input image
flowImage.encoding = sensor_msgs::image_encodings::TYPE_32FC2;
flowImage.image = _flow; //_flow is calculated elsewhere
flowImagePtr = flowImage.toImageMsg();
publishCvImage(flowImagePtr); //uses a publisher to publish image
}
The code works fine, but ROS is unable to display floating point images (I tried either image_view and rviz). What is the right way to convert those images to a displayable form?
Cheers, Tom.
Edit: Eventually I'm using these two methods to visualize the results (the below code bases on this):
cvImagePtr = cv_bridge::toCvCopy(image, sensor_msgs::image_encodings::RGB8);
flowImage = cvImagePtr->image; //original RGB captured image, needs to be converted to MONO8 for cv::calcOpticalFlowFarneback
cv::Mat velx = cv::Mat(flowImage.size(), CV_32FC1);
cv::Mat vely = cv::Mat(flowImage.size(), CV_32FC1);
cv::Mat flowMatrix; //outcome of cv::calcOpticalFlowFarneback
void OpticalFlow::extractFlowFieldFarneback() {
for (int row = 0; row < flowMatrix.rows; row++) {
float *vx = (float*)(velx.data + velx.step * row);
float *vy = (float*)(vely.data + vely.step * row);
const float* f = (const float*)(flowMatrix.data + flowMatrix.step * row);
for (int col = 0; col < flowMatrix.cols; col++) {
vx[col] = f[2*col];
vy[col] = f[2*col+1];
}
}
}
void OpticalFlow::overlayFlowFieldFarneback() {
if (!velx.empty() && !vely.empty()) {
for (int row = 0; row < flowImage.rows; row++) {
float *vx = (float*)(velx.data + velx.step * row);
float *vy = (float*)(vely.data + vely.step * row);
for (int col = 0; col < flowImage.cols; col++) {
double length = sqrt(vx[col]*vx[col] + vy[col]*vy[col]);
if (length > 1) {
cv::Point2i p1(col, row);
cv::Point2i p2(col + (int)vx[col], row + (int)vy[col]);
cv::line(flowImage, p1, p2, cvScalar(255, 0, 0, 0));
}
}
}
}
}