Internally, it is just a subscriber to the camera node. It is probably best to have worked through the ROS beginner tutorials before following this.
The data coming from the camera is in this format: http://www.ros.org/doc/api/sensor_msgs/html/msg/Image.html
We want to write it in ppm format: http://en.wikipedia.org/wiki/Netpbm_format#PPM_example
We will take the time stamp of the image and generate the file name as YYYYMMDDThhmms.ppm, with a small detour through posix time because ros::Time can't be converted directly to string.
More on posix time here: http://www.boost.org/doc/libs/1_53_0/doc/html/date_time/posix_time.html
First, create and build the package for this project (I've called it a very uninspired "screenshot", and by the time I realized I was not in fact taking screenshots I was almost finished, so there you have it), adding as dependency image_transport (you can also add it later via the manifest file):
$ roscreate-pkg screenshot std_msgs roscpp image_transport $ rosmake screenshotThen create the subscriber (mine is called "cam_listener"), not forgetting to add it to Cmakelists.txt:
$ rosbuild_add_executable(cam_listener src/cam_listener.cpp)The main() function in cam_listener.cpp looks like this:
int main(int argc, char **argv) { ros::init(argc, argv, "cam_listener"); ros::NodeHandle n; image_transport::ImageTransport it(n); image_transport::Subscriber sub = it.subscribe("camera/rgb/image_color", 1, imageCallback); while (true) { ros::spinOnce(); ros::Duration(15).sleep(); // Take screenshots every 15 seconds } return 0; }We use image_transport to create a bit more specialized subscriber, in order to get data from the camera. The callback is named "imageCallback", and it looks like this:
void imageCallback(const sensor_msgs::ImageConstPtr& msg) { // Writes the raw image data in a PPM formatted file // We convert the time stamp that comes in the header of the data from the camera to posix time, in order to use it as part of the ppm filename ros::Time stamp = msg->header.stamp; boost::posix_time::ptime posix_time = stamp.toBoost(); // We only take part of the posix time std::string filename = boost::posix_time::to_iso_string(posix_time).substr(0, 14) + ".ppm"; std::ofstream f(filename.c_str()); if (!f) { std::cout << "Couldn't open file.\n"; } std::cout << "Writing data to file " << filename << "\n"; // On the first line, the magic marker for a PPM file -- "P3" f << "P3\n"; // On the second line, the number of columns and the number of rows f << msg->width << " " << msg->height << "\n"; // The max colour value f << "255\n"; // Write the triplets of colour int row = 0; int pixel; while (row < msg->height) { pixel = row * msg->step; while (pixel < (row+1)*msg->step) { // B = pixel, G = pixel + 1, R = pixel + 2 -> we need RGB for the PPM format // Since we are working with unsigned char, cast it to int for writing to the file f << static_cast< int >(msg->data[pixel+2]) << " "; f << static_cast< int >(msg->data[pixel+1]) << " "; f << static_cast< int >(msg->data[pixel]) << " "; pixel += 3; // Jump to the next bgr triple } f << std::endl; row++; } f.close(); }Basically, we write the header of the ppm file and then the plain text values for the pixels. The only tricky aspect is the order of the rgb pixels. The encoding of the data from the image is bgr8 (check it yourself in msg->encoding!), whereas for the ppm file we need rgb. That's why, on lines 35-37, we take the values in reverse order.
In order to run the program, we start roscore in one terminal, and the openni camera in another terminal:
$ roslaunch openni_launch openni.launchIn a third terminal, we start our listener:
$ rosrun screenshot cam_listenerEvery 15 seconds, a shot from the camera will be saved in the