ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange |
1 | initial version |
Hi everybody,
so the solution is to take the extract_images file, and to modify it :
just modify the
bgr8
and
mono8
to
sensor_msgs::image_encodings::TYPE_32FC1
Don't forget to add at the top :
#include <sensor_msgs/image_encodings.h>
Last but not least add in your manifest
<depend package="cv_bridge"/>
Here is the whole code :
// modified version for depth extraction
/*********************************************************************
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of the Willow Garage nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*********************************************************************/
#include <opencv2/highgui/highgui.hpp>
#include <ros/ros.h>
#include <sensor_msgs/Image.h>
#include <sensor_msgs/image_encodings.h>
#include <cv_bridge/cv_bridge.h>
#include <image_transport/image_transport.h>
#include <boost/thread.hpp>
#include <boost/format.hpp>
class ExtractImages
{
private:
image_transport::Subscriber sub_;
sensor_msgs::ImageConstPtr last_msg_;
boost::mutex image_mutex_;
std::string window_name_;
boost::format filename_format_;
int count_;
double _time;
double sec_per_frame_;
#if defined(_VIDEO)
CvVideoWriter* video_writer;
#endif //_VIDEO
public:
ExtractImages(const ros::NodeHandle& nh, const std::string& transport)
: filename_format_(""), count_(0), _time(ros::Time::now().toSec())
{
std::string topic = nh.resolveName("image");
ros::NodeHandle local_nh("~");
std::string format_string;
local_nh.param("filename_format", format_string, std::string("frame%04i.jpg"));
filename_format_.parse(format_string);
local_nh.param("sec_per_frame", sec_per_frame_, 0.1);
image_transport::ImageTransport it(nh);
sub_ = it.subscribe(topic, 1, &ExtractImages::image_cb, this, transport);
#if defined(_VIDEO)
video_writer = 0;
#endif
ROS_INFO("Initialized sec per frame to %f", sec_per_frame_);
}
~ExtractImages()
{
}
void image_cb(const sensor_msgs::ImageConstPtr& msg)
{
boost::lock_guard<boost::mutex> guard(image_mutex_);
// Hang on to message pointer for sake of mouse_cb
last_msg_ = msg;
// May want to view raw bayer data
// NB: This is hacky, but should be OK since we have only one image CB.
if (msg->encoding.find("bayer") != std::string::npos)
boost::const_pointer_cast<sensor_msgs::Image>(msg)->encoding = sensor_msgs::image_encodings::TYPE_32FC1;
cv::Mat image;
try
{
image = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::TYPE_32FC1)->image;
} catch(cv_bridge::Exception)
{
ROS_ERROR("Unable to convert %s image to 32FC1", msg->encoding.c_str());
}
double delay = ros::Time::now().toSec()-_time;
if(delay >= sec_per_frame_)
{
_time = ros::Time::now().toSec();
if (!image.empty()) {
std::string filename = (filename_format_ % count_).str();
#if !defined(_VIDEO)
cv::imwrite(filename, image);
#else
if(!video_writer)
{
video_writer = cvCreateVideoWriter("video.avi", CV_FOURCC('M','J','P','G'),
int(1.0/sec_per_frame_), cvSize(image->width, image->height));
}
cvWriteFrame(video_writer, image);
#endif // _VIDEO
ROS_INFO("Saved image %s", filename.c_str());
count_++;
} else {
ROS_WARN("Couldn't save image, no data!");
}
}
}
};
int main(int argc, char **argv)
{
ros::init(argc, argv, "extract_images", ros::init_options::AnonymousName);
ros::NodeHandle n;
if (n.resolveName("image") == "/image") {
ROS_WARN("extract_images: image has not been remapped! Typical command-line usage:\n"
"\t$ ./extract_images image:=<image topic> [transport]");
}
ExtractImages view(n, (argc > 1) ? argv[1] : "raw");
ros::spin();
return 0;
}
Have a good day everyone,
Bests,
Stéphane
2 | No.2 Revision |
Hi everybody,
so the solution is to take the extract_images file, file from the package image_view, and to modify it :
just modify the
bgr8
and
mono8
to
sensor_msgs::image_encodings::TYPE_32FC1
Don't forget to add at the top :
#include <sensor_msgs/image_encodings.h>
Last but not least add in your manifest
<depend package="cv_bridge"/>
The results will be, as in the original file, jpg files. However, don't be frightened if they look black, because values are given in meter (for depth_registered/image).
Here is the whole code :
// modified version for depth extraction
/*********************************************************************
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of the Willow Garage nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*********************************************************************/
#include <opencv2/highgui/highgui.hpp>
#include <ros/ros.h>
#include <sensor_msgs/Image.h>
#include <sensor_msgs/image_encodings.h>
#include <cv_bridge/cv_bridge.h>
#include <image_transport/image_transport.h>
#include <boost/thread.hpp>
#include <boost/format.hpp>
class ExtractImages
{
private:
image_transport::Subscriber sub_;
sensor_msgs::ImageConstPtr last_msg_;
boost::mutex image_mutex_;
std::string window_name_;
boost::format filename_format_;
int count_;
double _time;
double sec_per_frame_;
#if defined(_VIDEO)
CvVideoWriter* video_writer;
#endif //_VIDEO
public:
ExtractImages(const ros::NodeHandle& nh, const std::string& transport)
: filename_format_(""), count_(0), _time(ros::Time::now().toSec())
{
std::string topic = nh.resolveName("image");
ros::NodeHandle local_nh("~");
std::string format_string;
local_nh.param("filename_format", format_string, std::string("frame%04i.jpg"));
filename_format_.parse(format_string);
local_nh.param("sec_per_frame", sec_per_frame_, 0.1);
image_transport::ImageTransport it(nh);
sub_ = it.subscribe(topic, 1, &ExtractImages::image_cb, this, transport);
#if defined(_VIDEO)
video_writer = 0;
#endif
ROS_INFO("Initialized sec per frame to %f", sec_per_frame_);
}
~ExtractImages()
{
}
void image_cb(const sensor_msgs::ImageConstPtr& msg)
{
boost::lock_guard<boost::mutex> guard(image_mutex_);
// Hang on to message pointer for sake of mouse_cb
last_msg_ = msg;
// May want to view raw bayer data
// NB: This is hacky, but should be OK since we have only one image CB.
if (msg->encoding.find("bayer") != std::string::npos)
boost::const_pointer_cast<sensor_msgs::Image>(msg)->encoding = sensor_msgs::image_encodings::TYPE_32FC1;
cv::Mat image;
try
{
image = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::TYPE_32FC1)->image;
} catch(cv_bridge::Exception)
{
ROS_ERROR("Unable to convert %s image to 32FC1", msg->encoding.c_str());
}
double delay = ros::Time::now().toSec()-_time;
if(delay >= sec_per_frame_)
{
_time = ros::Time::now().toSec();
if (!image.empty()) {
std::string filename = (filename_format_ % count_).str();
#if !defined(_VIDEO)
cv::imwrite(filename, image);
#else
if(!video_writer)
{
video_writer = cvCreateVideoWriter("video.avi", CV_FOURCC('M','J','P','G'),
int(1.0/sec_per_frame_), cvSize(image->width, image->height));
}
cvWriteFrame(video_writer, image);
#endif // _VIDEO
ROS_INFO("Saved image %s", filename.c_str());
count_++;
} else {
ROS_WARN("Couldn't save image, no data!");
}
}
}
};
int main(int argc, char **argv)
{
ros::init(argc, argv, "extract_images", ros::init_options::AnonymousName);
ros::NodeHandle n;
if (n.resolveName("image") == "/image") {
ROS_WARN("extract_images: image has not been remapped! Typical command-line usage:\n"
"\t$ ./extract_images image:=<image topic> [transport]");
}
ExtractImages view(n, (argc > 1) ? argv[1] : "raw");
ros::spin();
return 0;
}
Have a good day everyone,
Bests,
Stéphane
3 | EDIT : added a synchronized version for rgb and depth |
Hi everybody,
so the solution is to take the extract_images file from the package image_view, and to modify it :
just modify the
bgr8
and
mono8
to
sensor_msgs::image_encodings::TYPE_32FC1
Don't forget to add at the top :
#include <sensor_msgs/image_encodings.h>
Last but not least add in your manifest
<depend package="cv_bridge"/>
The results will be, as in the original file, jpg files. However, don't be frightened if they look black, because values are given in meter (for depth_registered/image).
Here is the whole code :
// modified version for depth extraction
/*********************************************************************
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Willow Garage, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of the Willow Garage nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*********************************************************************/
#include <opencv2/highgui/highgui.hpp>
#include <ros/ros.h>
#include <sensor_msgs/Image.h>
#include <sensor_msgs/image_encodings.h>
#include <cv_bridge/cv_bridge.h>
#include <image_transport/image_transport.h>
#include <boost/thread.hpp>
#include <boost/format.hpp>
class ExtractImages
{
private:
image_transport::Subscriber sub_;
sensor_msgs::ImageConstPtr last_msg_;
boost::mutex image_mutex_;
std::string window_name_;
boost::format filename_format_;
int count_;
double _time;
double sec_per_frame_;
#if defined(_VIDEO)
CvVideoWriter* video_writer;
#endif //_VIDEO
public:
ExtractImages(const ros::NodeHandle& nh, const std::string& transport)
: filename_format_(""), count_(0), _time(ros::Time::now().toSec())
{
std::string topic = nh.resolveName("image");
ros::NodeHandle local_nh("~");
std::string format_string;
local_nh.param("filename_format", format_string, std::string("frame%04i.jpg"));
filename_format_.parse(format_string);
local_nh.param("sec_per_frame", sec_per_frame_, 0.1);
image_transport::ImageTransport it(nh);
sub_ = it.subscribe(topic, 1, &ExtractImages::image_cb, this, transport);
#if defined(_VIDEO)
video_writer = 0;
#endif
ROS_INFO("Initialized sec per frame to %f", sec_per_frame_);
}
~ExtractImages()
{
}
void image_cb(const sensor_msgs::ImageConstPtr& msg)
{
boost::lock_guard<boost::mutex> guard(image_mutex_);
// Hang on to message pointer for sake of mouse_cb
last_msg_ = msg;
// May want to view raw bayer data
// NB: This is hacky, but should be OK since we have only one image CB.
if (msg->encoding.find("bayer") != std::string::npos)
boost::const_pointer_cast<sensor_msgs::Image>(msg)->encoding = sensor_msgs::image_encodings::TYPE_32FC1;
cv::Mat image;
try
{
image = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::TYPE_32FC1)->image;
} catch(cv_bridge::Exception)
{
ROS_ERROR("Unable to convert %s image to 32FC1", msg->encoding.c_str());
}
double delay = ros::Time::now().toSec()-_time;
if(delay >= sec_per_frame_)
{
_time = ros::Time::now().toSec();
if (!image.empty()) {
std::string filename = (filename_format_ % count_).str();
#if !defined(_VIDEO)
cv::imwrite(filename, image);
#else
if(!video_writer)
{
video_writer = cvCreateVideoWriter("video.avi", CV_FOURCC('M','J','P','G'),
int(1.0/sec_per_frame_), cvSize(image->width, image->height));
}
cvWriteFrame(video_writer, image);
#endif // _VIDEO
ROS_INFO("Saved image %s", filename.c_str());
count_++;
} else {
ROS_WARN("Couldn't save image, no data!");
}
}
}
};
int main(int argc, char **argv)
{
ros::init(argc, argv, "extract_images", ros::init_options::AnonymousName);
ros::NodeHandle n;
if (n.resolveName("image") == "/image") {
ROS_WARN("extract_images: image has not been remapped! Typical command-line usage:\n"
"\t$ ./extract_images image:=<image topic> [transport]");
}
ExtractImages view(n, (argc > 1) ? argv[1] : "raw");
ros::spin();
return 0;
}
Have a good day everyone,
Bests,
Stéphane
[EDIT] : SYNCHRONIZED VERSION for extracting rgb and depth at the same time. It may not be the best solution (using API would be better), but at least it works. I did not programm it (one of my colleague did this trick) and I do not clean the code, I let it "as it", and just hope it can help people.
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <iostream>
#include <sensor_msgs/Image.h>
#include <message_filters/subscriber.h>
#include <message_filters/synchronizer.h>
#include <message_filters/sync_policies/approximate_time.h>
#include <opencv2/highgui/highgui.hpp>
#include <sensor_msgs/image_encodings.h>
#include <cv_bridge/cv_bridge.h>
using namespace std;
// Initializing the counters
int count_rgb = 1;
int count_depth = 1;
double spf;
//
cv::Mat image1;
void depthToCV8UC1(const cv::Mat& float_img, cv::Mat& mono8_img);
//
// Function for converting the msg to jpg
void extraction(const sensor_msgs::ImageConstPtr& msg, int* count, ros::NodeHandle& node_handle)
{
//ROS_WARN("Entering extraction.");
// May want to view raw bayer data
// NB: This is hacky, but should be OK since we have only one image CB.
if (msg->encoding.find("bayer") != std::string::npos)
{
if (strcmp(msg->encoding.c_str(),"32FC1")==0)
{
boost::const_pointer_cast<sensor_msgs::Image>(msg)->encoding = sensor_msgs::image_encodings::TYPE_32FC1;
}else
{
boost::const_pointer_cast<sensor_msgs::Image>(msg)->encoding = "mono8";
}
}
cv::Mat image;
try
{
if (strcmp(msg->encoding.c_str(),"32FC1")==0)
{
//ROS_WARN("Sharing depth image.");
image = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::TYPE_32FC1)->image;
//
depthToCV8UC1(image, image1);
image = image1;
//
}else
{
//ROS_WARN("Sharing rgb image.");
image = cv_bridge::toCvShare(msg, "bgr8")->image;
}
} catch(cv_bridge::Exception)
{
if (strcmp(msg->encoding.c_str(),"32FC1")==0)
{
ROS_ERROR("Unable to convert %s image to 32FC1", msg->encoding.c_str());
}else
{
ROS_ERROR("Unable to convert %s image to bgr8", msg->encoding.c_str());
}
}
if (!image.empty()) {
//ROS_WARN("Image converted.");
std::string filename;
std::stringstream sstm;
std::ostringstream oss;
std::string path1_string;
node_handle.getParam("path1", path1_string);
std::string path2_string;
node_handle.getParam("path2", path2_string);
/*if (node_handle.hasParam("path"))
{
ROS_WARN("the path.c_str() is %s",path_string.c_str());
}
else
{
ROS_WARN("No path parameter found");
}*/
oss << *count;
if (strcmp(msg->encoding.c_str(),"32FC1")==0)
{
//ROS_WARN("Creating depth name.");
sstm << path1_string.c_str() << "depth_" << oss.str() << ".jpg";
}else
{
//ROS_WARN("Creating rgb name.");
sstm << path2_string.c_str() << "rgb_" << oss.str() << ".jpg";
}
filename = sstm.str();
cv::imwrite(filename, image);
ROS_INFO("Saved image %s", filename.c_str());
*count = *count +1;
} else {
ROS_WARN("Couldn't save image, no data!");
}
}
void callback(ros::NodeHandle& node_handle, const sensor_msgs::ImageConstPtr& Depth_msg, const sensor_msgs::ImageConstPtr& RGB_msg)
{
//ros::Rate loop_rate(30);
//ROS_WARN("Entering the callback.");
node_handle.getParam("sec_per_frame",spf);
//secperframe(node_handle,spf);
int a= 1.0/spf;
ros::Rate loop_rate(a);
//ROS_INFO("looprate %i",a);
//ROS_WARN("Starting extraction of depth.");
extraction(Depth_msg,&count_depth, node_handle);
//ROS_WARN("Starting extraction of rgb.");
extraction(RGB_msg,&count_rgb, node_handle);
}
int main(int argc, char **argv)
{
// Initialization
ros::init(argc, argv, "extraction_synchronized");
// Handler
ros::NodeHandle nh;
// Handler
ros::NodeHandle nh_priv("~");
// Suscribers to the RGB and depth kinect message
message_filters::Subscriber<sensor_msgs::Image> depth_sub(nh, "depth_image", 10);
ROS_INFO("%s", "Suscriber of Depth set");
message_filters::Subscriber<sensor_msgs::Image> rgb_sub(nh, "rgb_image", 10);
ROS_INFO("%s", "Suscriber of RGB set");
// Aproximate Time Synchronizer
typedef message_filters::sync_policies::ApproximateTime<sensor_msgs::Image, sensor_msgs::Image> MySyncPolicy;
// ApproximateTime takes a queue size as its constructor argument, hence MySyncPolicy(10)
message_filters::Synchronizer<MySyncPolicy> sync(MySyncPolicy(10), depth_sub, rgb_sub);
sync.registerCallback(boost::bind(&callback,nh_priv,_1, _2));
ROS_INFO("%s", "Depth and RGB synchronized");
ros::spin();
return 0;
}
// ADDED to have good conversion
void depthToCV8UC1(const cv::Mat& float_img, cv::Mat& mono8_img){
//Process images
if(mono8_img.rows != float_img.rows || mono8_img.cols != float_img.cols){
mono8_img = cv::Mat(float_img.size(), CV_8UC1);}
cv::convertScaleAbs(float_img, mono8_img, 100, 0.0);
}