screengrab_ros_node.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2013 Lucas Walter
00003  * November 2013
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions are met:
00008  *
00009  *     * Redistributions of source code must retain the above copyright
00010  *       notice, this list of conditions and the following disclaimer.
00011  *     * Redistributions in binary form must reproduce the above copyright
00012  *       notice, this list of conditions and the following disclaimer in the
00013  *       documentation and/or other materials provided with the distribution.
00014  *     * Neither the name of the Willow Garage, Inc. nor the names of its
00015  *       contributors may be used to endorse or promote products derived from
00016  *       this software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00019  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00020  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00021  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00022  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00023  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00024  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00025  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00026  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00027  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00028  * POSSIBILITY OF SUCH DAMAGE.
00029  */
00030 
00031 #include <dynamic_reconfigure/server.h>
00032 #include <nodelet/nodelet.h>
00033 #include <ros/ros.h>
00034 #include <screen_grab/ScreenGrabConfig.h>
00035 #include <sensor_msgs/Image.h>
00036 #include <sensor_msgs/image_encodings.h>
00037 #include <sensor_msgs/RegionOfInterest.h>
00038 
00039 // X Server includes
00040 #include <X11/Xlib.h>
00041 #include <X11/Xutil.h>
00042 
00043 void XImage2RosImage(XImage& ximage, Display& _xDisplay, Screen& _xScreen,
00044                      sensor_msgs::ImagePtr& im)
00045 {
00046   XColor color;
00047 
00048   im->header.stamp = ros::Time::now();
00049 
00050   if (_xScreen.depths->depth == 24)
00051   {
00052     // the code just deleted here is probably more robust than
00053     // a straight memcpy, but for now go without it.
00054     const int wd = ximage.width;
00055     const int ht = ximage.height;
00056     const int frame_size = wd * ht * 4;
00057     im->width = wd;
00058     im->height = ht;
00059     im->step = im->width * 4;
00060     // maybe this could be extracted from X
00061     im->encoding = sensor_msgs::image_encodings::BGRA8;
00062     im->data.resize(frame_size);
00063     memcpy(&im->data[0], ximage.data, frame_size);
00064   }
00065   else     // Extremly slow alternative for non 24bit-depth
00066   {
00067     Colormap colmap = DefaultColormap(&_xDisplay, DefaultScreen(&_xDisplay));
00068     for (unsigned int x = 0; x < ximage.width; x++)
00069     {
00070       for (unsigned int y = 0; y < ximage.height; y++)
00071       {
00072         color.pixel = XGetPixel(&ximage, x, y);
00073         XQueryColor(&_xDisplay, colmap, &color);
00074         // cv::Vec4b col = cv::Vec4b(color.blue, color.green, color.red,0);
00075         // tmp.at<cv::Vec4b> (y,x) = col;
00076       }
00077     }
00078   }
00079   return;
00080 }
00081 
00082 namespace screen_grab
00083 {
00084 
00085 class ScreenGrab : public nodelet::Nodelet
00086 {
00087   // ros::NodeHandle nh_;
00088 
00089   ros::Publisher screen_pub_;
00090 
00091   ros::Subscriber roi_sub_;
00092   void roiCallback(const sensor_msgs::RegionOfInterest::ConstPtr& msg);
00093 
00094   double update_rate_;
00095 
00096   typedef dynamic_reconfigure::Server<screen_grab::ScreenGrabConfig> ReconfigureServer;
00097   boost::shared_ptr< ReconfigureServer > server_;
00098   void callback(screen_grab::ScreenGrabConfig &config,
00099                 uint32_t level);
00100 
00101   void checkRoi(int& x_offset, int& y_offset, int& width, int& height);
00102   void updateConfig();
00103 
00104   int x_offset_;
00105   int y_offset_;
00106   int width_;
00107   int height_;
00108 
00109   int screen_w_;
00110   int screen_h_;
00111 
00112   boost::recursive_mutex dr_mutex_;
00113 
00114   void spinOnce(const ros::TimerEvent& e);
00115   bool first_error_;
00116 
00117   ros::Timer timer_;
00118 
00119   // X resources
00120   Display* display;
00121   Screen* screen;
00122   XImage* xImageSample;
00123   XColor col;
00124 
00125 public:
00126   virtual void onInit();
00127 
00128   ScreenGrab();
00129 
00130   bool spin();
00131 };
00132 }  //  namespace screen_grab
00133 
00134 #include <pluginlib/class_list_macros.h>
00135 
00136 PLUGINLIB_EXPORT_CLASS(screen_grab::ScreenGrab, nodelet::Nodelet)
00137 
00138 namespace screen_grab
00139 {
00140 
00141 ScreenGrab::ScreenGrab() :
00142   x_offset_(0),
00143   y_offset_(0),
00144   width_(640),
00145   height_(480),
00146   first_error_(false)
00147   // server_(dr_mutex_)  // this locks up
00148 {
00149 }
00150 
00151 void ScreenGrab::roiCallback(const sensor_msgs::RegionOfInterest::ConstPtr& msg)
00152 {
00153   x_offset_ = msg->x_offset;
00154   y_offset_ = msg->y_offset;
00155   width_ = msg->width;
00156   height_ = msg->height;
00157 
00158   updateConfig();
00159 }
00160 
00161 void ScreenGrab::checkRoi(int& x_offset, int& y_offset, int& width, int& height)
00162 {
00163   // TODO(lucasw) with cv::Rect this could be one line rect1 & rect2
00164 
00165   // Need to check against resolution
00166   if ((x_offset + width) > screen_w_)
00167   {
00168     // TBD need to more intelligently cap these
00169     if (screen_w_ > width)
00170     {
00171       x_offset = screen_w_ - width;
00172     }
00173     else
00174     {
00175       x_offset = 0;
00176       width = screen_w_;
00177     }
00178   }
00179 
00180   if ((y_offset + height) > screen_h_)
00181   {
00182     // TBD need to more intelligently cap these
00183     if (screen_h_ > height)
00184     {
00185       y_offset = screen_h_ - height;
00186     }
00187     else
00188     {
00189       y_offset = 0;
00190       height = screen_h_;
00191     }
00192   }
00193 
00194   // ROS_INFO_STREAM(x_offset << " " << y_offset << " " << width << " " << height);
00195 }
00196 
00197 void ScreenGrab::callback(
00198   screen_grab::ScreenGrabConfig &config,
00199   uint32_t level)
00200 {
00201   if (level & 1)
00202   {
00203     checkRoi(config.x_offset, config.y_offset, config.width, config.height);
00204     x_offset_ = config.x_offset;
00205     y_offset_ = config.y_offset;
00206     width_ = config.width;
00207     height_ = config.height;
00208   }
00209 
00210   if (level & 2)
00211   {
00212     if (config.update_rate != update_rate_)
00213     {
00214       update_rate_ = config.update_rate;
00215       // TODO(lucasw) update timer
00216     }
00217   }
00218 }
00219 
00220 void ScreenGrab::updateConfig()
00221 {
00222   checkRoi(x_offset_, y_offset_, width_, height_);
00223 
00224   // TODO(lucasw) just store config_ instead of x_offset_ etc.
00225   screen_grab::ScreenGrabConfig config;
00226   config.update_rate = update_rate_;
00227   config.x_offset = x_offset_;
00228   config.y_offset = y_offset_;
00229   config.width = width_;
00230   config.height = height_;
00231 
00232   server_->updateConfig(config);
00233 }
00234 
00235 void ScreenGrab::onInit()
00236 {
00237   screen_pub_ = getNodeHandle().advertise<sensor_msgs::Image>(
00238                   "image", 5);
00239   // TODO(lucasw) move most of this into onInit
00240   // init
00241   // from vimjay screencap.cpp (https://github.com/lucasw/vimjay)
00242   {
00243     display = XOpenDisplay(NULL);  // Open first (-best) display
00244     if (display == NULL)
00245     {
00246       ROS_ERROR_STREAM("bad display");
00247       return;
00248     }
00249 
00250     screen = DefaultScreenOfDisplay(display);
00251     if (screen == NULL)
00252     {
00253       ROS_ERROR_STREAM("bad screen");
00254       return;
00255     }
00256 
00257     Window wid = DefaultRootWindow(display);
00258     if (0 > wid)
00259     {
00260       ROS_ERROR_STREAM("Failed to obtain the root windows Id "
00261                        "of the default screen of given display.\n");
00262       return;
00263     }
00264 
00265     XWindowAttributes xwAttr;
00266     Status ret = XGetWindowAttributes(display, wid, &xwAttr);
00267     screen_w_ = xwAttr.width;
00268     screen_h_ = xwAttr.height;
00269   }
00270 
00271   double update_rate = 15;
00272   int x_offset = 0;
00273   int y_offset = 0;
00274   int width = 0;
00275   int height = 0;
00276 
00277   bool rv0 = getPrivateNodeHandle().getParam("update_rate", update_rate);
00278   bool rv1 = getPrivateNodeHandle().getParam("x_offset", x_offset);
00279   bool rv2 = getPrivateNodeHandle().getParam("y_offset", y_offset);
00280   bool rv3 = getPrivateNodeHandle().getParam("width", width);
00281   bool rv4 = getPrivateNodeHandle().getParam("height", height);
00282 
00283   ROS_INFO_STREAM(static_cast<int>(rv0) << static_cast<int>(rv1)
00284     << static_cast<int>(rv2) << static_cast<int>(rv3) << static_cast<int>(rv4));
00285   ROS_INFO_STREAM(update_rate << " " << width << " " << height);
00286   server_.reset(new ReconfigureServer(dr_mutex_, getPrivateNodeHandle()));
00287 
00288   dynamic_reconfigure::Server<screen_grab::ScreenGrabConfig>::CallbackType cbt =
00289     boost::bind(&ScreenGrab::callback, this, _1, _2);
00290   server_->setCallback(cbt);
00291 
00292   // TODO(lucasw) do I really need to do this, or does dr clobber my params?
00293   update_rate_ = update_rate;
00294   x_offset_ = x_offset;
00295   y_offset_ = y_offset;
00296   width_ = width;
00297   height_ = height;
00298   checkRoi(x_offset_, y_offset_, width_, height_);
00299   updateConfig();
00300 
00301   roi_sub_ = getPrivateNodeHandle().subscribe("roi", 0, &ScreenGrab::roiCallback, this);
00302 
00303   const float period = 1.0 / update_rate_;
00304   ROS_INFO_STREAM("period " << period);
00305   timer_ = getPrivateNodeHandle().createTimer(ros::Duration(period),
00306            &ScreenGrab::spinOnce, this);
00307 }
00308 
00309 void ScreenGrab::spinOnce(const ros::TimerEvent& e)
00310 {
00311   sensor_msgs::ImagePtr im(new sensor_msgs::Image);
00312 
00313   // grab the image
00314   xImageSample = XGetImage(display, DefaultRootWindow(display),
00315                            x_offset_, y_offset_, width_, height_, AllPlanes, ZPixmap);
00316 
00317   // Check for bad null pointers
00318   if (xImageSample == NULL)
00319   {
00320     if (first_error_)
00321       ROS_ERROR_STREAM("Error taking screenshot! "
00322                        << ", " << x_offset_ << " " << y_offset_
00323                        << ", " << width_ << " " << height_
00324                        << ", " << screen_w_ << " " << screen_h_);
00325     first_error_ = false;
00326     return;
00327   }
00328 
00329   if (!first_error_)
00330     ROS_INFO_STREAM(width_ << " " << height_);
00331   first_error_ = true;
00332   // convert to Image format
00333   XImage2RosImage(*xImageSample, *display, *screen, im);
00334 
00335   XDestroyImage(xImageSample);
00336 
00337 
00338 
00339   screen_pub_.publish(im);
00340 }
00341 }  // namespace screen_grab
00342 
00343 #if 0
00344 int main(int argc, char **argv)
00345 {
00346   ros::init(argc, argv, "screen_grab");
00347 
00348   ScreenGrab screen_grab;
00349   screen_grab.spin();
00350 }
00351 #endif
00352 


screen_grab
Author(s):
autogenerated on Fri Feb 12 2016 00:23:26