virtual_camera_mono.cpp
Go to the documentation of this file.
00001 #include <ros/ros.h>
00002 #include <jsk_topic_tools/log_utils.h>
00003 #include <image_transport/image_transport.h>
00004 #include <image_geometry/pinhole_camera_model.h>
00005 #include <tf/transform_listener.h>
00006 #include <tf/transform_broadcaster.h>
00007 #include <opencv/cv.hpp>
00008 #include <opencv/highgui.h>
00009 #include <cv_bridge/cv_bridge.h>
00010 
00011 #include <vector>
00012 #include <boost/assign.hpp>
00013 
00014 #include <geometry_msgs/Polygon.h>
00015 #include <geometry_msgs/PolygonStamped.h>
00016 
00017 class VirtualCameraMono
00018 {
00019   ros::NodeHandle nh_,private_nh_;
00020   image_transport::ImageTransport it_,it_priv_;
00021   image_transport::CameraSubscriber sub_;
00022   image_transport::CameraPublisher pub_;
00023   int subscriber_count_;
00024   tf::TransformListener tf_listener_;
00025   image_geometry::PinholeCameraModel cam_model_;
00026   tf::TransformBroadcaster tf_broadcaster_;
00027   ros::Subscriber sub_trans_, sub_poly_;
00028 
00029   tf::StampedTransform trans_; // transform to virtual camera
00030   geometry_msgs::PolygonStamped poly_; // target polygon to transform image
00031 
00032 public:
00033   VirtualCameraMono() : private_nh_("~"), it_(nh_), it_priv_(private_nh_), subscriber_count_(0)
00034   {
00035     image_transport::SubscriberStatusCallback connect_cb = boost::bind(&VirtualCameraMono::connectCb, this, _1);
00036     image_transport::SubscriberStatusCallback disconnect_cb = boost::bind(&VirtualCameraMono::disconnectCb, this, _1);
00037     pub_ = it_priv_.advertiseCamera("image", 1, connect_cb, disconnect_cb);
00038 
00039     // init parameters : TODO replace ros::param
00040     trans_.frame_id_ = "/elevator_inside_panel";
00041     trans_.child_frame_id_ = "/virtual_camera_frame";
00042     trans_.setOrigin(tf::Vector3(0.7,0,0));
00043     trans_.setRotation(tf::Quaternion(0.707,0,0,-0.707) * tf::Quaternion(0,0.707,0,-0.707));
00044 
00045     poly_.header.frame_id = "/elevator_inside_panel";
00046     geometry_msgs::Point32 pt;
00047     pt.x=0, pt.y=1, pt.z=0; poly_.polygon.points.push_back(pt);
00048     pt.x=0, pt.y=-1, pt.z=0; poly_.polygon.points.push_back(pt);
00049     pt.x=0, pt.y=-1, pt.z=-1; poly_.polygon.points.push_back(pt);
00050     pt.x=0, pt.y=1, pt.z=-1; poly_.polygon.points.push_back(pt);
00051 
00052     // parameter subscriber
00053     sub_trans_ = nh_.subscribe<geometry_msgs::TransformStamped>("view_point", 1, &VirtualCameraMono::transCb, this);
00054     sub_poly_ = nh_.subscribe<geometry_msgs::PolygonStamped>("target_polygon", 1, &VirtualCameraMono::polyCb, this);
00055 
00056   }
00057 
00058   void connectCb(const image_transport::SingleSubscriberPublisher&)
00059   {
00060     if (subscriber_count_ == 0){
00061       subscribe();
00062     }
00063     subscriber_count_++;
00064     ROS_DEBUG("connected new node. current subscriber: %d", subscriber_count_);
00065   }
00066 
00067   void disconnectCb(const image_transport::SingleSubscriberPublisher&)
00068   {
00069     subscriber_count_--;
00070     if (subscriber_count_ == 0) {
00071       unsubscribe();
00072     }
00073     ROS_DEBUG("disconnected node. current subscriber: %d", subscriber_count_);
00074   }
00075 
00076   void subscribe()
00077   {
00078     ROS_INFO("Subscribing to image topic");
00079     sub_ = it_.subscribeCamera("image", 1, &VirtualCameraMono::imageCb, this);
00080     ros::V_string names = boost::assign::list_of("image");
00081     jsk_topic_tools::warnNoRemap(names);
00082   }
00083 
00084   void unsubscribe()
00085   {
00086     ROS_INFO("Unsubscibing from image topic");
00087     sub_.shutdown();
00088   }
00089 
00090   void imageCb(const sensor_msgs::ImageConstPtr& image_msg,
00091                const sensor_msgs::CameraInfoConstPtr& info_msg)
00092   {
00093     cv_bridge::CvImagePtr cv_ptr;
00094     cv::Mat image;
00095     try {
00096       cv_ptr = cv_bridge::toCvCopy(image_msg, "bgr8");
00097       image = cv_ptr->image;
00098     }
00099     catch (cv_bridge::Exception& ex) {
00100       ROS_ERROR("[virtual_camera_mono] Failed to convert image");
00101       return;
00102     }
00103 
00104     cam_model_.fromCameraInfo(info_msg);
00105 
00106     trans_.stamp_ = ros::Time::now();
00107     tf_broadcaster_.sendTransform(trans_);
00108 
00109     //
00110     ROS_DEBUG("transform image.");
00111     //IplImage *outimage = cvCloneImage(image); // need to release
00112     cv::Mat outimage = image.clone();
00113     if (TransformImage(image, outimage, trans_, poly_, cam_model_)) {
00114       //
00115       ROS_DEBUG("publish image and transform.");
00116       sensor_msgs::CameraInfo virtual_info = *info_msg;
00117       //sensor_msgs::Image::Ptr img_msg = bridge_.cvToImgMsg(outimage, "bgr8");
00118       cv_ptr->image = outimage;
00119       sensor_msgs::Image::Ptr img_msg = cv_ptr->toImageMsg();
00120       img_msg->header.stamp = trans_.stamp_;
00121       virtual_info.header.stamp = trans_.stamp_;
00122       img_msg->header.frame_id = trans_.child_frame_id_;
00123       virtual_info.header.frame_id = trans_.child_frame_id_;
00124       pub_.publish(*img_msg, virtual_info);
00125     }
00126 
00127     // finalize
00128     //cvReleaseImage(&outimage);
00129   }
00130 
00131   // subscribe target polygon
00132   void polyCb(const geometry_msgs::PolygonStampedConstPtr& poly) { poly_ = *poly; }
00133   // subscribe virtual camera pose
00134   void transCb(const geometry_msgs::TransformStampedConstPtr& tf) {
00135     trans_.frame_id_ = tf->header.frame_id;
00136     trans_.child_frame_id_ = tf->child_frame_id;
00137     trans_.setOrigin(tf::Vector3(tf->transform.translation.x,
00138                                  tf->transform.translation.y,
00139                                  tf->transform.translation.z));
00140     trans_.setRotation(tf::Quaternion(tf->transform.rotation.x, tf->transform.rotation.y,
00141                                       tf->transform.rotation.z, tf->transform.rotation.w));
00142   }
00143 
00144   // poly is plane only
00145   bool TransformImage(cv::Mat src, cv::Mat dest,
00146                       tf::StampedTransform& trans, geometry_msgs::PolygonStamped& poly,
00147                       image_geometry::PinholeCameraModel& cam_model_)
00148   {
00149     try {
00150       // transform polygon to camera coordinate and virtual camera coordinate
00151       std::vector<tf::Point> target_poly, target_poly_translated;
00152       for(std::vector<geometry_msgs::Point32>::iterator pit=poly.polygon.points.begin(); pit != poly.polygon.points.end(); pit++) {
00153         geometry_msgs::PointStamped point, cpoint, vpoint;
00154         point.point.x = pit->x, point.point.y = pit->y, point.point.z = pit->z;
00155         point.header = poly.header;
00156         tf_listener_.transformPoint(cam_model_.tfFrame(), point, cpoint);
00157         tf_listener_.transformPoint(trans.frame_id_, point, vpoint);
00158 
00159         tf::Vector3 vpt_vec = trans.inverse() * tf::Vector3(vpoint.point.x, vpoint.point.y, vpoint.point.z);
00160 
00161         // push
00162         target_poly.push_back(tf::Point(cpoint.point.x, cpoint.point.y, cpoint.point.z));
00163         target_poly_translated.push_back(tf::Point(vpt_vec.x(),vpt_vec.y(),vpt_vec.z()));
00164       }
00165 
00166       // warp from (cpoint in camera) to (vpoint in virtual camera)
00167       cv::Point2f src_pnt[4], dst_pnt[4];
00168       for(int i = 0; i < 4; i++) {
00169         cv::Point3d xyz(target_poly[i].x(),target_poly[i].y(),target_poly[i].z());
00170         cv::Point3d xyz_trans(target_poly_translated[i].x(),
00171                               target_poly_translated[i].y(),
00172                               target_poly_translated[i].z());
00173         cv::Point2d uv,uv_trans;
00174         uv = cam_model_.project3dToPixel(xyz);
00175         src_pnt[i] = cv::Point (uv.x, uv.y);
00176         uv_trans = cam_model_.project3dToPixel(xyz_trans);
00177         dst_pnt[i] = cv::Point (uv_trans.x, uv_trans.y);
00178       }
00179 
00180       cv::Mat map_matrix = cv::getPerspectiveTransform (src_pnt, dst_pnt);
00181 
00182       // unrectified?
00183       //IplImage* rectified = cvCloneImage(src);
00184       //cv::Mat from_mat = cv::Mat(src), to_mat = cv::Mat(rectified);
00185       //cam_model_.rectifyImage(from_mat,to_mat);
00186       //cvWarpPerspective (rectified, dest, map_matrix, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvScalarAll (0));
00187       cv::Mat to_mat = src.clone();
00188       cam_model_.rectifyImage(src, to_mat);
00189       cv::warpPerspective (src, dest, map_matrix, dest.size(), cv::INTER_LINEAR);
00190       //cvReleaseImage(&rectified);
00191     } catch ( std::runtime_error e ) {
00192       ROS_INFO_THROTTLE(10, "[virtual_camera_mono] failed to transform image: %s", e.what());
00193       return false;
00194     }
00195     return true;
00196   }
00197 };
00198 
00199 int main(int argc, char **argv)
00200 {
00201   ros::init(argc, argv, "virtual_camera_mono");
00202 
00203   VirtualCameraMono vcam;
00204 
00205   ros::spin();
00206 }
00207 


jsk_perception
Author(s): Manabu Saito, Ryohei Ueda
autogenerated on Tue Jul 2 2019 19:41:07