# Revision history [back]

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>


<depend package="cv_bridge"/>


Here is the whole code :

// modified version for depth extraction

/*********************************************************************
*
* Copyright (c) 2008, Willow Garage, Inc.
*
* 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/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 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

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>


<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

/*********************************************************************
*
* Copyright (c) 2008, Willow Garage, Inc.
*
* 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/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);
}