LadybugPanoNodelet.cpp
Go to the documentation of this file.
00001 
00002 #include <boost/make_shared.hpp>
00003 #include <ros/ros.h>
00004 #include <ros/package.h>
00005 #include <nodelet/nodelet.h>
00006 #include <pluginlib/class_list_macros.h>
00007 #include <cv_bridge/cv_bridge.h>
00008 #include <image_transport/image_transport.h>
00009 #include <opencv2/core/core.hpp>
00010 #include <opencv2/imgproc/imgproc.hpp>
00011 #include <sensor_msgs/image_encodings.h>
00012 #include <tf/transform_broadcaster.h>
00013 #include <tf/transform_datatypes.h>
00014 
00015 #include "omnicamera/LookupStitcher.h"
00016 
00017 //#define USE_MT_NODE_HANDLE
00018 
00019 namespace omnicamera {
00020 
00021 class LadybugPanoNodelet : public nodelet::Nodelet {
00022 public:
00023   LadybugPanoNodelet();
00024   void onInit();
00025   void imageCallback(const sensor_msgs::ImageConstPtr& msg);
00026 private:
00027   int inWidth;
00028   int inHeight;
00029   int outWidth;
00030   int outHeight;
00031   double pan;
00032   string omnicamFrameId;
00033   string frameId;
00034   bool alpha;
00035   int radius;
00036   string lutPath;
00037   omnicamera::LookupStitcher stitcher;
00041   boost::shared_ptr<image_transport::ImageTransport> it;
00042   image_transport::Subscriber imageSub;
00043   image_transport::Publisher outPub;
00044   ros::Publisher camPub;
00045   bool lookupTablesLoaded;
00046   tf::TransformBroadcaster tfBroadcaster;
00047 };
00048 
00049 LadybugPanoNodelet::LadybugPanoNodelet() {
00050 }
00051 
00052 void LadybugPanoNodelet::onInit() {
00053   NODELET_INFO("Initializing LadybugPanoNodelet...");
00054 #ifdef USE_MT_NODE_HANDLE
00055   ros::NodeHandle &nh = getMTNodeHandle();
00056   ros::NodeHandle &pnh = getMTPrivateNodeHandle();
00057 #else
00058   ros::NodeHandle &nh = getNodeHandle();
00059   ros::NodeHandle &pnh = getPrivateNodeHandle();
00060 #endif
00061 
00062   pnh.param("in_width", inWidth, 1616);
00063   pnh.param("in_height", inHeight, 1232);
00064   NODELET_INFO("Input image size: %dx%d.", inWidth, inHeight);
00065 
00066   pnh.param("out_width", outWidth, 960);
00067   pnh.param("out_height", outHeight, 480);
00068   pnh.param("pan", pan, 0.0);
00069   pnh.param("frame_id", frameId, string("pano"));
00070   pnh.param("omnicam_frame_id", omnicamFrameId, string("omnicam"));
00071   NODELET_INFO("Output image size: %dx%d, frame id: %s, omnicam frame id: %s.", outWidth, outHeight, frameId.data(), omnicamFrameId.data());
00072 
00073   pnh.param("alpha", alpha, true);
00074   NODELET_INFO("Alpha blending: %s.", alpha ? "enabled" : "disabled");
00075 
00076   pnh.param("radius", radius, 20);
00077   NODELET_INFO("Sphere radius: %d.", radius);
00078 
00079   std::string omnicameraPath = ros::package::getPath("omnicamera");
00080   pnh.param("lut_format", lutPath, string("/res/lut/LUT_img_%dx%d_pano_%dx%dr%d_%s%d.png"));
00081   lutPath = omnicameraPath + lutPath;
00082   NODELET_INFO("LUT path format: %s.", lutPath.data());
00083   // Loading look-up tables postponed to first callback to return fast.
00084   lookupTablesLoaded = false;
00085 
00086 //  image_transport::ImageTransport it(nh);
00087   it.reset(new image_transport::ImageTransport(nh));
00088   imageSub = it->subscribe("in", 1, &LadybugPanoNodelet::imageCallback, this);
00089   outPub = it->advertise("out", 1);
00090   camPub = nh.advertise<sensor_msgs::CameraInfo>("camera_info", 1);
00091 }
00092 
00093 void LadybugPanoNodelet::imageCallback(const sensor_msgs::ImageConstPtr& msg) {
00094 //   NODELET_INFO("LadybugPanoNodelet::imageCallback: entering.");
00095 
00096   if (!lookupTablesLoaded) {
00097     NODELET_INFO("Loading look-up tables...");
00098     stitcher.clearLookupTables();
00099     for (int iCam = 1; iCam <= 6; iCam++) {
00100       int len = 1024;
00101       char xPath[len], yPath[len], aPath[len];
00102       snprintf(xPath, len, lutPath.data(), inWidth, inHeight, outWidth, outHeight, radius, "x", iCam);
00103       snprintf(yPath, len, lutPath.data(), inWidth, inHeight, outWidth, outHeight, radius, "y", iCam);
00104       snprintf(aPath, len, lutPath.data(), inWidth, inHeight, outWidth, outHeight, radius, "a", iCam);
00105       NODELET_INFO("Camera %d look-up tables: %s, %s, %s.", iCam, xPath, yPath, aPath);
00106       stitcher.addLookupTables(string(xPath), string(yPath), string(aPath));
00107     }
00108     stitcher.fixBounds(inWidth, inHeight);
00109 
00110     // Apply pan if needed by shifting look-up tables.
00111     int shiftRight = round(outWidth * pan / 360.0);
00112     if (shiftRight != 0) {
00113       NODELET_INFO("Using pan %f. Shifting look-up tables right by %d pixels.", pan, shiftRight);
00114       cv::Mat tempLut, tempColRange;
00115       int cols = stitcher.getLutCount() > 0 ? stitcher.lutX[0].cols : 0;
00116       for (int i = 0; shiftRight != 0 && i < stitcher.getLutCount(); i++) {
00117         if (shiftRight > 0) {
00118           // Shift look-up tables right.
00119           // Ensure appropriate dimensions and type.
00120           stitcher.lutA[i].copyTo(tempLut);
00121           // First fill temporary matrix.
00122           NODELET_DEBUG("Valid range <%d, %d>.", 0, cols - 1);
00123           NODELET_DEBUG("Copy <%d, %d> to <%d, %d>", 0, cols - shiftRight - 1, shiftRight, cols - 1);
00124           NODELET_DEBUG("Copy <%d, %d> to <%d, %d>", cols - shiftRight, cols - 1, 0, shiftRight - 1);
00125           tempColRange = tempLut.colRange(shiftRight, cols - 1);
00126           stitcher.lutA[i].colRange(0, cols - shiftRight - 1).copyTo(tempColRange);
00127           tempColRange = tempLut.colRange(0, shiftRight - 1);
00128           stitcher.lutA[i].colRange(cols - shiftRight, cols - 1).copyTo(tempColRange);
00129           // Then copy back to the working look-up table.
00130           tempLut.copyTo(stitcher.lutA[i]);
00131 
00132           stitcher.lutX[i].copyTo(tempLut);
00133           tempColRange = tempLut.colRange(shiftRight, cols - 1);
00134           stitcher.lutX[i].colRange(0, cols - shiftRight - 1).copyTo(tempColRange);
00135           tempColRange = tempLut.colRange(0, shiftRight - 1);
00136           stitcher.lutX[i].colRange(cols - shiftRight, cols - 1).copyTo(tempColRange);
00137           tempLut.copyTo(stitcher.lutX[i]);
00138 
00139           stitcher.lutY[i].copyTo(tempLut);
00140           tempColRange = tempLut.colRange(shiftRight, cols - 1);
00141           stitcher.lutY[i].colRange(0, cols - shiftRight - 1).copyTo(tempColRange);
00142           tempColRange = tempLut.colRange(0, shiftRight - 1);
00143           stitcher.lutY[i].colRange(cols - shiftRight, cols - 1).copyTo(tempColRange);
00144           tempLut.copyTo(stitcher.lutY[i]);
00145 
00146         } else {
00147           // Shift look-up tables left.
00148           int shiftLeft = -shiftRight;
00149           stitcher.lutA[i].copyTo(tempLut);
00150           NODELET_DEBUG("Valid range <%d, %d>.", 0, cols - 1);
00151           NODELET_DEBUG("Copy <%d, %d> to <%d, %d>", shiftLeft, cols - 1, 0, cols - shiftLeft - 1);
00152           NODELET_DEBUG("Copy <%d, %d> to <%d, %d>", 0, shiftLeft - 1, cols - shiftLeft, cols - 1);
00153           tempColRange = tempLut.colRange(0, cols - shiftLeft - 1);
00154           stitcher.lutA[i].colRange(shiftLeft, cols - 1).copyTo(tempColRange);
00155           tempColRange = tempLut.colRange(cols - shiftLeft, cols - 1);
00156           stitcher.lutA[i].colRange(0, shiftLeft - 1).copyTo(tempColRange);
00157           tempLut.copyTo(stitcher.lutA[i]);
00158 
00159           stitcher.lutX[i].copyTo(tempLut);
00160           tempColRange = tempLut.colRange(0, cols - shiftLeft - 1);
00161           stitcher.lutX[i].colRange(shiftLeft, cols - 1).copyTo(tempColRange);
00162           tempColRange = tempLut.colRange(cols - shiftLeft, cols - 1);
00163           stitcher.lutX[i].colRange(0, shiftLeft - 1).copyTo(tempColRange);
00164           tempLut.copyTo(stitcher.lutX[i]);
00165 
00166           stitcher.lutY[i].copyTo(tempLut);
00167           tempColRange = tempLut.colRange(0, cols - shiftLeft - 1);
00168           stitcher.lutY[i].colRange(shiftLeft, cols - 1).copyTo(tempColRange);
00169           tempColRange = tempLut.colRange(cols - shiftLeft, cols - 1);
00170           stitcher.lutY[i].colRange(0, shiftLeft - 1).copyTo(tempColRange);
00171           tempLut.copyTo(stitcher.lutY[i]);
00172         }
00173       }
00174     }
00175 
00176     lookupTablesLoaded = true;
00177   }
00178 
00179   if (outPub.getNumSubscribers() == 0 && camPub.getNumSubscribers() == 0) {
00180     return;
00181   }
00182 
00183   cv_bridge::CvImageConstPtr cvPtr;
00184   try {
00185     cvPtr = cv_bridge::toCvShare(msg);
00186     if (cvPtr->encoding.compare(sensor_msgs::image_encodings::BGR8)) {
00187       NODELET_ERROR("Encoding not supported: %s.", cvPtr->encoding.data());
00188       return;
00189     }
00190   } catch (cv_bridge::Exception& e) {
00191     NODELET_ERROR("cv_bridge exception: %s", e.what());
00192     return;
00193   }
00194 
00195   if (cvPtr->image.rows != inHeight || cvPtr->image.cols != inWidth) {
00196     NODELET_ERROR("Unexpected image dimensions: %dx%d.", cvPtr->image.cols, cvPtr->image.rows);
00197   }
00198 
00199   Mat image;
00200   NODELET_DEBUG("Stitching started.");
00201   stitcher.stitchImage(cvPtr->image, image);
00202   NODELET_DEBUG("Stitching finished.");
00203 
00204   tf::Transform transform;
00205   double realPan = 2 * M_PI * round(outWidth * pan / 360.0) / outWidth;
00206   transform.setRotation(tf::createQuaternionFromRPY(0, 0, realPan));
00207   tfBroadcaster.sendTransform(tf::StampedTransform(transform, msg->header.stamp, omnicamFrameId, frameId));
00208 
00209   cv_bridge::CvImage out;
00210   out.encoding = sensor_msgs::image_encodings::BGR8;
00211   out.header = msg->header;
00212   out.header.frame_id = frameId;
00213   out.image = image;
00214   outPub.publish(out.toImageMsg());
00215 
00216   sensor_msgs::CameraInfo camMsg;
00217   camMsg.header = out.header;
00218   camMsg.height = out.image.rows;
00219   camMsg.width = out.image.cols;
00220   camPub.publish(camMsg);
00221 }
00222 
00223 } // namespace omnicamera
00224 
00225 PLUGINLIB_DECLARE_CLASS(omnicamera, ladybug_pano, omnicamera::LadybugPanoNodelet, nodelet::Nodelet)
 All Classes Namespaces Files Functions Variables Typedefs Defines


omnicamera
Author(s): Tomas Petricek / petrito1@cmp.felk.cvut.cz
autogenerated on Tue Dec 10 2013 14:26:53