image_flip.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright 2017 Fraunhofer Institute for Manufacturing Engineering and Automation (IPA)
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *   http://www.apache.org/licenses/LICENSE-2.0
00009 
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016  
00017 
00018 //##################
00019 //#### includes ####
00020 
00021 #include <cob_image_flip/image_flip.h>
00022 #include <geometry_msgs/TransformStamped.h>
00023 
00024 namespace cob_image_flip
00025 {
00026 ImageFlip::ImageFlip(ros::NodeHandle nh)
00027 :       node_handle_(nh), img_sub_counter_(0), pc_sub_counter_(0), disparity_sub_counter_(0), transform_listener_(nh), it_(0), last_rotation_angle_(0), last_rotation_factor_(0)
00028 {
00029         // set parameters
00030         ROS_DEBUG_STREAM("\n--------------------------\nImage Flip Parameters:\n--------------------------");
00031         node_handle_.param("rotation_mode", rotation_mode_, 0);
00032         ROS_DEBUG_STREAM("rotation_mode = " << rotation_mode_);
00033         if (rotation_mode_ == FIXED_ANGLE)
00034         {
00035                 // fixed rotation angle
00036                 node_handle_.param("rotation_angle", rotation_angle_, 0.);
00037                 ROS_DEBUG_STREAM("rotation_angle = " << rotation_angle_);
00038         }
00039         else if (rotation_mode_ == AUTOMATIC_GRAVITY_DIRECTION || rotation_mode_ == AUTOMATIC_GRAVITY_DIRECTION_90)
00040         {
00041                 // automatic rotation in gravity direction
00042                 node_handle_.param("reference_frame", reference_frame_, std::string(""));
00043                 ROS_DEBUG_STREAM("reference_frame = " << reference_frame_);
00044         }
00045         else
00046         {
00047                 ROS_ERROR("Unsupported value for parameter 'rotation_mode'. Exiting the cob_image_flip.");
00048                 exit(1);
00049         }
00050         node_handle_.param("flip_color_image", flip_color_image_, false);
00051         ROS_DEBUG_STREAM("flip_color_image = " << flip_color_image_);
00052         node_handle_.param("flip_pointcloud", flip_pointcloud_, false);
00053         ROS_DEBUG_STREAM("flip_pointcloud = " << flip_pointcloud_);
00054         node_handle_.param("flip_disparity_image", flip_disparity_image_, false);
00055         ROS_DEBUG_STREAM("flip_disparity_image = " << flip_disparity_image_);
00056         node_handle_.param("display_warnings", display_warnings_, false);
00057         ROS_DEBUG_STREAM("display_warnings = " << display_warnings_);
00058 
00059         if (flip_color_image_ == true)
00060         {
00061                 it_ = new image_transport::ImageTransport(node_handle_);
00062                 color_camera_image_sub_.registerCallback(boost::bind(&ImageFlip::imageCallback, this, _1));
00063                 color_camera_image_pub_ = it_->advertise("colorimage_out", 1, boost::bind(&ImageFlip::imgConnectCB, this, _1), boost::bind(&ImageFlip::imgDisconnectCB, this, _1));
00064                 color_camera_image_2d_transform_pub_ = node_handle_.advertise<cob_perception_msgs::Float64ArrayStamped>("colorimage_inplane_transform", 1,false);
00065         }
00066 
00067         // point cloud flip
00068         if (flip_pointcloud_ == true)
00069         {
00070                 point_cloud_pub_ = node_handle_.advertise<sensor_msgs::PointCloud2>("pointcloud_out", 1,  boost::bind(&ImageFlip::pcConnectCB, this, _1), boost::bind(&ImageFlip::pcDisconnectCB, this, _1));
00071                 point_cloud_2d_transform_pub_ = node_handle_.advertise<cob_perception_msgs::Float64ArrayStamped>("pointcloud_inplane_transform", 1,false);
00072         }
00073 
00074         if (flip_disparity_image_ == true)
00075         {
00076                 disparity_image_pub_ = node_handle_.advertise<stereo_msgs::DisparityImage>("disparityimage_out", 1,  boost::bind(&ImageFlip::disparityConnectCB, this, _1), boost::bind(&ImageFlip::disparityDisconnectCB, this, _1));
00077                 disparity_image_2d_transform_pub_ = node_handle_.advertise<cob_perception_msgs::Float64ArrayStamped>("disparityimage_inplane_transform", 1,false);
00078         }
00079 
00080         ROS_DEBUG_STREAM("ImageFlip initialized.");
00081 }
00082 
00083 ImageFlip::~ImageFlip()
00084 {
00085         if (it_ != 0)
00086                 delete it_;
00087 }
00088 
00089 
00090 double ImageFlip::determineRotationAngle(const std::string& camera_frame_id, const ros::Time& time)
00091 {
00092         double rotation_angle = 0.;
00093         if (rotation_mode_ == FIXED_ANGLE)
00094         {
00095                 rotation_angle = rotation_angle_;
00096         }
00097         else if (rotation_mode_ == AUTOMATIC_GRAVITY_DIRECTION || rotation_mode_ == AUTOMATIC_GRAVITY_DIRECTION_90)
00098         {
00099                 // check camera link orientation and decide whether image must be turned around
00100                 try
00101                 {
00102                         // compute angle of camera x-axis against x-y world plane (i.e. in reference coordinates)
00103                         tf::Stamped<tf::Vector3> x_axis_camera(tf::Vector3(1, 0, 0), time /*ros::Time(0)*/, camera_frame_id), x_axis_ref;
00104                         tf::Stamped<tf::Vector3> y_axis_camera(tf::Vector3(0, 1, 0), x_axis_camera.stamp_, camera_frame_id), y_axis_ref;
00105                         transform_listener_.waitForTransform(reference_frame_, camera_frame_id, x_axis_camera.stamp_, ros::Duration(0.2));
00106                         transform_listener_.transformVector(reference_frame_, x_axis_camera, x_axis_ref);
00107                         transform_listener_.transformVector(reference_frame_, y_axis_camera, y_axis_ref);
00108                         // compute the rotation angle so that the x-axis of the rotated image has 0 z-coordinate in the reference coordinate system (i.e. x-axis of rotated image is then parallel to the ground)
00109                         // the rotation is around the z-axis of the camera system
00110                         // 1. Compute the intersection between
00111                         //    E_1: x-y-plane in reference system coordinates: z=0
00112                         //    E_2: x-y-plane of camera system (but without translation, just the rotational part): [x,y,z] = [0,0,0] + r*[x_axis_ref.x,x_axis_ref.y,x_axis_ref.z] + s*[y_axis_ref.x,y_axis_ref.y,y_axis_ref.z]
00113                         //    --> r = -s*y_axis_ref.z/x_axis_ref.z
00114                         //    --> line equation: x = s*[y_axis_ref.x-x_axis_ref.x*y_axis_ref.z/x_axis_ref.z,y_axis_ref.y-x_axis_ref.y*y_axis_ref.z/x_axis_ref.z,0]
00115                         // 2. Compute the target camera x-axis, which is parallel to the ground. The vector for x is given by the line equation, the direction of the target y-axis decides about the direction (+ or -).
00116                         // 3. Compute the rotation between camera x-axis and target camera x-axis (i.e. between x_axis_ref and x_axis_target)
00117                         if (x_axis_ref.z()!=0)          // do not compute a rotation if the camera's x-axis is already correctly aligned
00118                         {
00119                                 // 1. line intersection
00120                                 const double a = y_axis_ref.z()/x_axis_ref.z();
00121                                 tf::Vector3 x_axis_target(y_axis_ref.x()-x_axis_ref.x()*a,y_axis_ref.y()-x_axis_ref.y()*a, 0);          // this is the line of intersection
00122                                 x_axis_target.normalize();
00123                                 // 2. resolve direction ambiguity
00124                                 tf::Vector3 z_axis_target = x_axis_ref.cross(y_axis_ref);       // remark: z_axis_ref == z_axis_target
00125                                 tf::Vector3 y_axis_target = z_axis_target.cross(x_axis_target);
00126                                 y_axis_target.normalize();
00127 
00128                                 //std::cout << "\n\nx_axis_ref=" << x_axis_ref.x() << ", " << x_axis_ref.y() << ", " << x_axis_ref.z()
00129                                 //              << "\ny_axis_ref=" << y_axis_ref.x() << ", " << y_axis_ref.y() << ", " << y_axis_ref.z()
00130                                 //              << "\nx_axis_target=" << x_axis_target.x() << ", " << x_axis_target.y() << ", " << x_axis_target.z()
00131                                 //              << "\ny_axis_target=" << y_axis_target.x() << ", " << y_axis_target.y() << ", " << y_axis_target.z() << "\n" << std::endl;
00132 
00133                                 // compute a factor than rotates the image in a way that the new y-axis in the rotated image directs against the z-direction of the reference system (i.e. points downwards)
00134                                 int factor = (y_axis_target.z()<0. ? 1 : -1);
00135                                 if (factor != last_rotation_factor_ && fabs(y_axis_target.z()) < 0.01)          // this hysteresis stops continuous flipping near 0
00136                                         factor = last_rotation_factor_;
00137                                 last_rotation_factor_ = factor;
00138                                 x_axis_target *= factor;
00139 
00140                                 //std::cout << "x_axis_target factored=" << x_axis_target.x() << ", " << x_axis_target.y() << ", " << x_axis_target.z() << "\n" << std::endl;
00141 
00142                                 // 3. compute angle
00143                                 tf::Vector3 rot_axis_x = x_axis_ref.cross(x_axis_target);
00144                                 double rot_sin = ((rot_axis_x.dot(z_axis_target)) >= 0 ? 1 : -1) * rot_axis_x.length();         // sign of sin() depends on alignment of cross-product rotation axis with z-axis
00145                                 double rot_cos = x_axis_ref.dot(x_axis_target);
00146                                 rotation_angle = -180./CV_PI * atan2(rot_sin, rot_cos);
00147 
00148                                 //std::cout << "rot_sin=" << rot_sin << "\trot_cos=" << rot_cos << "\trotation_angle=" << rotation_angle << "\n=========================================\n" << std::endl;
00149                         }
00150                         if (rotation_mode_ == AUTOMATIC_GRAVITY_DIRECTION_90)
00151                                 rotation_angle = 90. * cvRound(rotation_angle*1./90.);
00152                         last_rotation_angle_ = rotation_angle;
00153 
00154 //                      tf::StampedTransform transform;
00155 //                      transform_listener_.lookupTransform(reference_frame_, camera_frame_, ros::Time(0), transform);
00156 //                      tfScalar roll, pitch, yaw;
00157 //                      transform.getBasis().getRPY(roll, pitch, yaw, 1);
00158 //                      std::cout << "xyz: " << transform.getOrigin().getX() << " " << transform.getOrigin().getY() << " " << transform.getOrigin().getZ() << "\n";
00159 //                      std::cout << "abcw: " << transform.getRotation().getX() << " " << transform.getRotation().getY() << " " << transform.getRotation().getZ() << " " << transform.getRotation().getW() << "\n";
00160                 }
00161                 catch (tf2::TransformException& ex)
00162                 {
00163                         if (display_warnings_ == true)
00164                                 ROS_DEBUG("%s",ex.what());
00165                         rotation_angle = last_rotation_angle_;
00166                 }
00167         }
00168         else
00169         {
00170                 if (display_warnings_)
00171                         ROS_WARN("ImageFlip::imageCallback: Unsupported rotation mode.");
00172         }
00173 
00174         return rotation_angle;
00175 }
00176 
00177 
00178 bool ImageFlip::convertImageMessageToMat(const sensor_msgs::Image::ConstPtr& image_msg, cv_bridge::CvImageConstPtr& image_ptr, cv::Mat& image)
00179 {
00180         try
00181         {
00182                 image_ptr = cv_bridge::toCvShare(image_msg, image_msg->encoding);
00183         }
00184         catch (cv_bridge::Exception& e)
00185         {
00186                 ROS_ERROR("ImageFlip::convertColorImageMessageToMat: cv_bridge exception: %s", e.what());
00187                 return false;
00188         }
00189         image = image_ptr->image;
00190 
00191         return true;
00192 }
00193 
00194 
00195 void ImageFlip::imageCallback(const sensor_msgs::ImageConstPtr& color_image_msg)
00196 {
00197         // read image
00198         cv_bridge::CvImageConstPtr color_image_ptr;
00199         cv::Mat color_image;
00200         if (convertImageMessageToMat(color_image_msg, color_image_ptr, color_image) == false)
00201                 return;
00202         cv::Mat color_image_turned;
00203         cv::Mat rot_mat = cv::Mat::zeros(2,3,CV_64FC1);
00204 
00205         // determine rotation angle
00206         double rotation_angle = determineRotationAngle(color_image_msg->header.frame_id, color_image_msg->header.stamp);
00207 
00208         // rotate
00209         // fast hard coded rotations
00210         if (rotation_angle==0. || rotation_angle==360. || rotation_angle==-360.)
00211         {
00212                 color_image_turned = color_image;
00213                 rot_mat.at<double>(0,0) = 1.;
00214                 rot_mat.at<double>(1,1) = 1.;
00215         }
00216         else if (rotation_angle==90. || rotation_angle==-270.)
00217         {
00218                 // rotate images by 90 degrees
00219                 color_image_turned.create(color_image.cols, color_image.rows, color_image.type());
00220                 if (color_image.type() != CV_8UC3)
00221                 {
00222                         ROS_ERROR("ImageFlip::imageCallback: Error: The image format of the color image is not CV_8UC3.\n");
00223                         return;
00224                 }
00225                 for (int v = 0; v < color_image_turned.rows; v++)
00226                         for (int u = 0; u < color_image_turned.cols; u++)
00227                                 color_image_turned.at<cv::Vec3b>(v,u) = color_image.at<cv::Vec3b>(color_image.rows-1-u,v);
00228                 rot_mat.at<double>(0,1) = -1.;
00229                 rot_mat.at<double>(0,2) = color_image.rows;
00230                 rot_mat.at<double>(1,0) = 1.;
00231         }
00232         else if (rotation_angle==270. || rotation_angle==-90.)
00233         {
00234                 // rotate images by 270 degrees
00235                 color_image_turned.create(color_image.cols, color_image.rows, color_image.type());
00236                 if (color_image.type() != CV_8UC3)
00237                 {
00238                         std::cout << "ImageFlip::imageCallback: Error: The image format of the color image is not CV_8UC3.\n";
00239                         return;
00240                 }
00241                 for (int v = 0; v < color_image_turned.rows; v++)
00242                         for (int u = 0; u < color_image_turned.cols; u++)
00243                                 color_image_turned.at<cv::Vec3b>(v,u) = color_image.at<cv::Vec3b>(u,color_image.cols-1-v);
00244                 rot_mat.at<double>(0,1) = 1.;
00245                 rot_mat.at<double>(1,0) = -1.;
00246                 rot_mat.at<double>(1,2) = color_image.cols;
00247         }
00248         else if (rotation_angle==180 || rotation_angle==-180)
00249         {
00250                 // rotate images by 180 degrees
00251                 color_image_turned.create(color_image.rows, color_image.cols, color_image.type());
00252                 if (color_image.type() != CV_8UC3)
00253                 {
00254                         std::cout << "ImageFlip::imageCallback: Error: The image format of the color image is not CV_8UC3.\n";
00255                         return;
00256                 }
00257                 for (int v = 0; v < color_image.rows; v++)
00258                 {
00259                         uchar* src = color_image.ptr(v);
00260                         uchar* dst = color_image_turned.ptr(color_image.rows - v - 1) + 3 * (color_image.cols - 1);
00261                         for (int u = 0; u < color_image.cols; u++)
00262                         {
00263                                 for (int k = 0; k < 3; k++)
00264                                 {
00265                                         *dst = *src;
00266                                         src++;
00267                                         dst++;
00268                                 }
00269                                 dst -= 6;
00270                         }
00271                 }
00272                 rot_mat.at<double>(0,0) = -1.;
00273                 rot_mat.at<double>(0,2) = color_image.cols;
00274                 rot_mat.at<double>(1,1) = -1.;
00275                 rot_mat.at<double>(1,2) = color_image.rows;
00276         }
00277         else
00278         {
00279                 // arbitrary rotation
00280                 bool switch_aspect_ratio = !(fabs(sin(rotation_angle*CV_PI/180.)) < 0.707106781);
00281                 if (switch_aspect_ratio==false)
00282                         color_image_turned.create(color_image.rows, color_image.cols, color_image.type());              // automatically decide for landscape or portrait orientation of resulting image
00283                 else
00284                         color_image_turned.create(color_image.cols, color_image.rows, color_image.type());
00285                 if (color_image.type() != CV_8UC3)
00286                 {
00287                         ROS_ERROR("ImageFlip::imageCallback: Error: The image format of the color image is not CV_8UC3.\n");
00288                         return;
00289                 }
00290 
00291                 cv::Point center = cv::Point(color_image.cols/2, color_image.rows/2);
00292                 rot_mat = cv::getRotationMatrix2D(center, -rotation_angle, 1.0);
00293                 if (switch_aspect_ratio==true)
00294                 {
00295                         rot_mat.at<double>(0,2) += 0.5*(color_image_turned.cols-color_image.cols);
00296                         rot_mat.at<double>(1,2) += 0.5*(color_image_turned.rows-color_image.rows);
00297                 }
00298                 cv::warpAffine(color_image, color_image_turned, rot_mat, color_image_turned.size());
00299         }
00300 
00301         // publish turned image
00302         cv_bridge::CvImage cv_ptr;
00303         cv_ptr.image = color_image_turned;
00304         cv_ptr.encoding = color_image_msg->encoding;
00305         sensor_msgs::Image::Ptr color_image_turned_msg = cv_ptr.toImageMsg();
00306         color_image_turned_msg->header = color_image_msg->header;
00307         color_camera_image_pub_.publish(color_image_turned_msg);
00308 
00309         // publish rotation matrix for backward transform to non-turned coordinates using a homogeneous image coordinate representation
00310         // the original, non-turned coordinates are of interest if the camera calibration shall be used (the calibration does not apply to coordinates of the turned image)
00311         cv::Mat rot33 = cv::Mat::eye(3,3,CV_64FC1);
00312         for (int r=0; r<2; ++r)
00313                 for (int c=0; c<3; ++c)
00314                         rot33.at<double>(r,c) = rot_mat.at<double>(r,c);
00315         cv::Mat rot33_inv = rot33.inv();
00316         cob_perception_msgs::Float64ArrayStamped rot33_inv_msg;
00317         rot33_inv_msg.header = color_image_msg->header;
00318         rot33_inv_msg.data.resize(9);
00319         for (int r=0; r<3; ++r)
00320                 for (int c=0; c<3; ++c)
00321                         rot33_inv_msg.data[r*3+c] = rot33_inv.at<double>(r,c);
00322         color_camera_image_2d_transform_pub_.publish(rot33_inv_msg);
00323 }
00324 
00325 void ImageFlip::imgConnectCB(const image_transport::SingleSubscriberPublisher& pub)
00326 {
00327         img_sub_counter_++;
00328         if (img_sub_counter_ == 1)
00329         {
00330                 ROS_DEBUG("ImageFlip::imgConnectCB: Connecting image callback.");
00331                 color_camera_image_sub_.subscribe(*it_, "colorimage_in", 1);
00332         }
00333 }
00334 
00335 void ImageFlip::imgDisconnectCB(const image_transport::SingleSubscriberPublisher& pub)
00336 {
00337         img_sub_counter_--;
00338         if (img_sub_counter_ == 0)
00339         {
00340                 ROS_DEBUG("ImageFlip::imgDisconnectCB: Disconnecting image callback.");
00341                 color_camera_image_sub_.unsubscribe();
00342         }
00343 }
00344 
00345 
00346 void ImageFlip::pcCallback(const sensor_msgs::PointCloud2::ConstPtr& point_cloud_msg)
00347 {
00348         // determine rotation angle
00349         double rotation_angle = determineRotationAngle(point_cloud_msg->header.frame_id, point_cloud_msg->header.stamp);
00350         cv::Mat rot_mat = cv::Mat::zeros(2,3,CV_64FC1);
00351 
00352         // prepare turned point cloud
00353         const int element_size = point_cloud_msg->point_step;   // length of point in bytes
00354         const int row_size = point_cloud_msg->row_step;         // length of row in bytes
00355         sensor_msgs::PointCloud2::Ptr point_cloud_out_msg = boost::shared_ptr<sensor_msgs::PointCloud2>(new sensor_msgs::PointCloud2);
00356         point_cloud_out_msg->fields = point_cloud_msg->fields;
00357         point_cloud_out_msg->header = point_cloud_msg->header;
00358         point_cloud_out_msg->height = point_cloud_msg->height;
00359         point_cloud_out_msg->width = point_cloud_msg->width;
00360         point_cloud_out_msg->point_step = point_cloud_msg->point_step;
00361         point_cloud_out_msg->row_step = point_cloud_msg->row_step;
00362         point_cloud_out_msg->is_bigendian = point_cloud_msg->is_bigendian;
00363         point_cloud_out_msg->is_dense = point_cloud_msg->is_dense;
00364         point_cloud_out_msg->data.resize(point_cloud_out_msg->height * point_cloud_out_msg->width * element_size);
00365 
00366         // rotate
00367         // fast hard coded rotations
00368         if (rotation_angle==0. || rotation_angle==360. || rotation_angle==-360.)
00369         {
00370                 memcpy(&(point_cloud_out_msg->data[0]), &(point_cloud_msg->data[0]), point_cloud_msg->height*point_cloud_msg->width*element_size);
00371                 rot_mat.at<double>(0,0) = 1.;
00372                 rot_mat.at<double>(1,1) = 1.;
00373         }
00374         else if (rotation_angle==90. || rotation_angle==-270.)
00375         {
00376                 // rotate images by 90 degrees
00377                 point_cloud_out_msg->height = point_cloud_msg->width;
00378                 point_cloud_out_msg->width = point_cloud_msg->height;
00379                 const int row_size_out = point_cloud_msg->height*element_size;          // length of row in bytes
00380                 point_cloud_out_msg->row_step = row_size_out;
00381                 for (int v = 0; v < point_cloud_out_msg->height; v++)
00382                         for (int u = 0; u < point_cloud_out_msg->width; u++)
00383                                 memcpy(&(point_cloud_out_msg->data[v*row_size_out+u*element_size]), &(point_cloud_msg->data[(point_cloud_msg->height-1-u)*row_size+v*element_size]), element_size);
00384                 rot_mat.at<double>(0,1) = -1.;
00385                 rot_mat.at<double>(0,2) = point_cloud_msg->height;
00386                 rot_mat.at<double>(1,0) = 1.;
00387         }
00388         else if (rotation_angle==270. || rotation_angle==-90.)
00389         {
00390                 // rotate images by 270 degrees
00391                 point_cloud_out_msg->height = point_cloud_msg->width;
00392                 point_cloud_out_msg->width = point_cloud_msg->height;
00393                 const int row_size_out = point_cloud_msg->height*element_size;          // length of row in bytes
00394                 point_cloud_out_msg->row_step = row_size_out;
00395                 for (int v = 0; v < point_cloud_out_msg->height; v++)
00396                         for (int u = 0; u < point_cloud_out_msg->width; u++)
00397                                 memcpy(&(point_cloud_out_msg->data[v*row_size_out+u*element_size]), &(point_cloud_msg->data[u*row_size+(point_cloud_msg->width-1-v)*element_size]), element_size);
00398                 rot_mat.at<double>(0,1) = 1.;
00399                 rot_mat.at<double>(1,0) = -1.;
00400                 rot_mat.at<double>(1,2) = point_cloud_msg->width;
00401         }
00402         else if (rotation_angle==180 || rotation_angle==-180)
00403         {
00404                 // rotate images by 180 degrees
00405                 for (int v = 0; v < point_cloud_out_msg->height; v++)
00406                         for (int u = 0; u < point_cloud_out_msg->width; u++)
00407                                 memcpy(&(point_cloud_out_msg->data[v*row_size+u*element_size]), &(point_cloud_msg->data[(point_cloud_msg->height-1-v)*row_size+(point_cloud_msg->width-1-u)*element_size]), element_size);
00408                 rot_mat.at<double>(0,0) = -1.;
00409                 rot_mat.at<double>(0,2) = point_cloud_msg->width;
00410                 rot_mat.at<double>(1,1) = -1.;
00411                 rot_mat.at<double>(1,2) = point_cloud_msg->height;
00412         }
00413         else
00414         {
00415                 // arbitrary rotation
00416                 int row_size_out = row_size;
00417                 // automatically decide for landscape or portrait orientation of resulting image
00418                 bool switch_aspect_ratio = !(fabs(sin(rotation_angle*CV_PI/180.)) < 0.707106781);
00419                 if (switch_aspect_ratio==true)
00420                 {
00421                         point_cloud_out_msg->height = point_cloud_msg->width;
00422                         point_cloud_out_msg->width = point_cloud_msg->height;
00423                         row_size_out = point_cloud_msg->height*element_size;            // length of row in bytes
00424                         point_cloud_out_msg->row_step = row_size_out;
00425                 }
00426 
00427                 // compute transform
00428                 cv::Point center = cv::Point(point_cloud_msg->width/2, point_cloud_msg->height/2);
00429                 rot_mat = cv::getRotationMatrix2D(center, -rotation_angle, 1.0);
00430                 if (switch_aspect_ratio==true)
00431                 {
00432                         rot_mat.at<double>(0,2) += 0.5*((double)point_cloud_out_msg->width - (double)point_cloud_msg->width);
00433                         rot_mat.at<double>(1,2) += 0.5*((double)point_cloud_out_msg->height - (double)point_cloud_msg->height);
00434                 }
00435                 cv::Mat rot_mat_inv = rot_mat.clone();
00436                 rot_mat_inv.at<double>(0,1) = rot_mat.at<double>(1,0);
00437                 rot_mat_inv.at<double>(1,0) = rot_mat.at<double>(0,1);
00438                 rot_mat_inv.at<double>(0,2) = -rot_mat.at<double>(0,2)*rot_mat.at<double>(0,0) - rot_mat.at<double>(1,2)*rot_mat.at<double>(1,0);
00439                 rot_mat_inv.at<double>(1,2) = -rot_mat.at<double>(1,2)*rot_mat.at<double>(0,0) + rot_mat.at<double>(0,2)*rot_mat.at<double>(1,0);
00440 
00441                 // zero element
00442                 std::vector<uchar> zero_element(element_size, 0);
00443                 for (size_t i=0; i<point_cloud_msg->fields.size(); ++i)
00444                 {
00445                         if (point_cloud_msg->fields[i].name.compare("x") == 0 || point_cloud_msg->fields[i].name.compare("y") == 0 || point_cloud_msg->fields[i].name.compare("z") == 0)
00446                         {
00447                                 float* val = (float*)&(zero_element[point_cloud_msg->fields[i].offset]);
00448                                 *val = std::numeric_limits<float>::quiet_NaN();
00449                         }
00450                         if (point_cloud_msg->fields[i].name.compare("rgb") == 0)
00451                         {
00452                                 float* val = (float*)&(zero_element[point_cloud_msg->fields[i].offset]);
00453                                 int vali = 0;
00454                                 *val = *(float*)(&vali);
00455                         }
00456                 }
00457 
00458                 // warp (nearest neighbor mode, no interpolation)
00459                 for (int v = 0; v < point_cloud_out_msg->height; v++)
00460                 {
00461                         for (int u = 0; u < point_cloud_out_msg->width; u++)
00462                         {
00463                                 int src_u = rot_mat_inv.at<double>(0,0)*u + rot_mat_inv.at<double>(0,1)*v + rot_mat_inv.at<double>(0,2);
00464                                 int src_v = rot_mat_inv.at<double>(1,0)*u + rot_mat_inv.at<double>(1,1)*v + rot_mat_inv.at<double>(1,2);
00465                                 if (src_u < 0 || src_u > point_cloud_msg->width-1 || src_v < 0 || src_v > point_cloud_msg->height-1)
00466                                         memcpy(&(point_cloud_out_msg->data[v*row_size_out+u*element_size]), &(zero_element[0]), element_size);
00467                                 else
00468                                         memcpy(&(point_cloud_out_msg->data[v*row_size_out+u*element_size]), &(point_cloud_msg->data[src_v*row_size+src_u*element_size]), element_size);
00469                         }
00470                 }
00471         }
00472 
00473         // publish turned point cloud
00474         point_cloud_pub_.publish(point_cloud_out_msg);
00475 
00476         // publish rotation matrix for backward transform to non-turned coordinates using a homogeneous image coordinate representation
00477         // the original, non-turned coordinates are of interest if the camera calibration shall be used (the calibration does not apply to coordinates of the turned image)
00478         cv::Mat rot33 = cv::Mat::eye(3,3,CV_64FC1);
00479         for (int r=0; r<2; ++r)
00480                 for (int c=0; c<3; ++c)
00481                         rot33.at<double>(r,c) = rot_mat.at<double>(r,c);
00482         cv::Mat rot33_inv = rot33.inv();
00483         cob_perception_msgs::Float64ArrayStamped rot33_inv_msg;
00484         rot33_inv_msg.header = point_cloud_msg->header;
00485         rot33_inv_msg.data.resize(9);
00486         for (int r=0; r<3; ++r)
00487                 for (int c=0; c<3; ++c)
00488                         rot33_inv_msg.data[r*3+c] = rot33_inv.at<double>(r,c);
00489         point_cloud_2d_transform_pub_.publish(rot33_inv_msg);
00490 
00491 
00492 //      // check camera link orientation and decide whether image must be turned around
00493 //      bool turnAround = false;
00494 //      tf::StampedTransform transform;
00495 //      try
00496 //      {
00497 //              transform_listener_->lookupTransform("/base_link", "/head_cam3d_link", ros::Time(0), transform);
00498 //              tfScalar roll, pitch, yaw;
00499 //              transform.getBasis().getRPY(roll, pitch, yaw, 1);
00500 //              if (roll > 0.0)
00501 //                      turnAround = true;
00502 //              //      std::cout << "xyz: " << transform.getOrigin().getX() << " " << transform.getOrigin().getY() << " " << transform.getOrigin().getZ() << "\n";
00503 //              //      std::cout << "abcw: " << transform.getRotation().getX() << " " << transform.getRotation().getY() << " " << transform.getRotation().getZ() << " " << transform.getRotation().getW() << "\n";
00504 //              //      std::cout << "rpy: " << roll << " " << pitch << " " << yaw << "\n";
00505 //      } catch (tf::TransformException ex)
00506 //      {
00507 //              if (display_warnings_ == true)
00508 //                      ROS_WARN("%s",ex.what());
00509 //      }
00510 //
00511 //      if (turnAround == false)
00512 //      {
00513 //              // image upright
00514 //              //sensor_msgs::Image color_image_turned_msg = *color_image_msg;
00515 //              //color_image_turned_msg.header.stamp = ros::Time::now();
00516 //              //color_camera_image_pub_.publish(color_image_turned_msg);
00517 //              //sensor_msgs::PointCloud2::ConstPtr point_cloud_turned_msg = point_cloud_msg;
00518 //              point_cloud_pub_.publish(point_cloud_msg);
00519 //      }
00520 //      else
00521 //      {
00522 //              // image upside down
00523 //              // point cloud
00524 //              pcl::PointCloud<T> point_cloud_src;
00525 //              pcl::fromROSMsg(*point_cloud_msg, point_cloud_src);
00526 //
00527 //              boost::shared_ptr<pcl::PointCloud<T> > point_cloud_turned(new pcl::PointCloud<T>);
00528 //              //point_cloud_turned->header = point_cloud_msg->header;
00529 //              point_cloud_turned->header = pcl_conversions::toPCL(point_cloud_msg->header);
00530 //              point_cloud_turned->height = point_cloud_msg->height;
00531 //              point_cloud_turned->width = point_cloud_msg->width;
00532 //              //point_cloud_turned->sensor_orientation_ = point_cloud_msg->sensor_orientation_;
00533 //              //point_cloud_turned->sensor_origin_ = point_cloud_msg->sensor_origin_;
00534 //              point_cloud_turned->is_dense = true;    //point_cloud_msg->is_dense;
00535 //              point_cloud_turned->resize(point_cloud_src.height*point_cloud_src.width);
00536 //              for (int v = (int)point_cloud_src.height - 1; v >= 0; v--)
00537 //              {
00538 //                      for (int u = (int)point_cloud_src.width - 1; u >= 0; u--)
00539 //                      {
00540 //                              (*point_cloud_turned)(point_cloud_src.width-1 - u, point_cloud_src.height-1 - v) = point_cloud_src(u, v);
00541 //                      }
00542 //              }
00543 //
00544 //              // publish turned data
00545 //              sensor_msgs::PointCloud2::Ptr point_cloud_turned_msg(new sensor_msgs::PointCloud2);
00546 //              pcl::toROSMsg(*point_cloud_turned, *point_cloud_turned_msg);
00547 //              //point_cloud_turned_msg->header.stamp = ros::Time::now();
00548 //              point_cloud_pub_.publish(point_cloud_turned_msg);
00549 //
00550 //              //      cv::namedWindow("test");
00551 //              //      cv::imshow("test", color_image_turned);
00552 //              //      cv::waitKey(10);
00553 //      }
00554 //
00555 //      if (display_timing_ == true)
00556 //              ROS_INFO("%d ImageFlip: Time stamp of pointcloud message: %f. Delay: %f.", point_cloud_msg->header.seq, point_cloud_msg->header.stamp.toSec(), ros::Time::now().toSec()-point_cloud_msg->header.stamp.toSec());
00557 }
00558 
00559 void ImageFlip::pcConnectCB(const ros::SingleSubscriberPublisher& pub)
00560 {
00561         pc_sub_counter_++;
00562         if (pc_sub_counter_ == 1)
00563         {
00564                 ROS_DEBUG("ImageFlip::pcConnectCB: Connecting point cloud callback.");
00565                 point_cloud_sub_ = node_handle_.subscribe<sensor_msgs::PointCloud2>("pointcloud_in", 1, &ImageFlip::pcCallback, this);
00566         }
00567 }
00568 
00569 void ImageFlip::pcDisconnectCB(const ros::SingleSubscriberPublisher& pub)
00570 {
00571         pc_sub_counter_--;
00572         if (pc_sub_counter_ == 0)
00573         {
00574                 ROS_DEBUG("ImageFlip::pcDisconnectCB: Disconnecting point cloud callback.");
00575                 point_cloud_sub_.shutdown();
00576         }
00577 }
00578 
00579 void void_delete_image_msg(const sensor_msgs::Image*)
00580 {
00581         return;
00582 }
00583 
00584 void ImageFlip::disparityCallback(const stereo_msgs::DisparityImage::ConstPtr& disparity_image_msg)
00585 {
00586         // read image
00587         cv_bridge::CvImageConstPtr disparity_image_ptr;
00588         cv::Mat disparity_image;
00589         sensor_msgs::ImageConstPtr disparity_image_constptr = boost::shared_ptr<const sensor_msgs::Image>(&disparity_image_msg->image, void_delete_image_msg);
00590         if (convertImageMessageToMat(disparity_image_constptr, disparity_image_ptr, disparity_image) == false)
00591                 return;
00592         cv::Mat disparity_image_turned;
00593         cv::Mat rot_mat = cv::Mat::zeros(2,3,CV_64FC1);
00594 
00595         // determine rotation angle
00596         double rotation_angle = determineRotationAngle(disparity_image_msg->header.frame_id, disparity_image_msg->header.stamp);
00597 
00598         // rotate
00599         // fast hard coded rotations
00600         if (rotation_angle==0. || rotation_angle==360. || rotation_angle==-360.)
00601         {
00602                 disparity_image_turned = disparity_image;
00603                 rot_mat.at<double>(0,0) = 1.;
00604                 rot_mat.at<double>(1,1) = 1.;
00605         }
00606         else if (rotation_angle==90. || rotation_angle==-270.)
00607         {
00608                 // rotate images by 90 degrees
00609                 disparity_image_turned.create(disparity_image.cols, disparity_image.rows, disparity_image.type());
00610                 if (disparity_image.type() != CV_32FC1)
00611                 {
00612                         ROS_ERROR("ImageFlip::imageCallback: Error: The image format of the disparity image is not CV_32FC1.\n");
00613                         return;
00614                 }
00615                 for (int v = 0; v < disparity_image_turned.rows; v++)
00616                         for (int u = 0; u < disparity_image_turned.cols; u++)
00617                                 disparity_image_turned.at<float>(v,u) = disparity_image.at<float>(disparity_image.rows-1-u,v);
00618                 rot_mat.at<double>(0,1) = -1.;
00619                 rot_mat.at<double>(0,2) = disparity_image.rows;
00620                 rot_mat.at<double>(1,0) = 1.;
00621         }
00622         else if (rotation_angle==270. || rotation_angle==-90.)
00623         {
00624                 // rotate images by 270 degrees
00625                 disparity_image_turned.create(disparity_image.cols, disparity_image.rows, disparity_image.type());
00626                 if (disparity_image.type() != CV_32FC1)
00627                 {
00628                         std::cout << "ImageFlip::imageCallback: Error: The image format of the color image is not CV_32FC1.\n";
00629                         return;
00630                 }
00631                 for (int v = 0; v < disparity_image_turned.rows; v++)
00632                         for (int u = 0; u < disparity_image_turned.cols; u++)
00633                                 disparity_image_turned.at<float>(v,u) = disparity_image.at<float>(u,disparity_image.cols-1-v);
00634                 rot_mat.at<double>(0,1) = 1.;
00635                 rot_mat.at<double>(1,0) = -1.;
00636                 rot_mat.at<double>(1,2) = disparity_image.cols;
00637         }
00638         else if (rotation_angle==180 || rotation_angle==-180)
00639         {
00640                 // rotate images by 180 degrees
00641                 disparity_image_turned.create(disparity_image.rows, disparity_image.cols, disparity_image.type());
00642                 if (disparity_image.type() != CV_32FC1)
00643                 {
00644                         std::cout << "ImageFlip::imageCallback: Error: The image format of the color image is not CV_32FC1.\n";
00645                         return;
00646                 }
00647                 for (int v = 0; v < disparity_image.rows; v++)
00648                 {
00649                         float* src = (float*)disparity_image.ptr(v);
00650                         float* dst = (float*)disparity_image_turned.ptr(disparity_image.rows - v - 1) + (disparity_image.cols - 1);
00651                         for (int u = 0; u < disparity_image.cols; u++)
00652                         {
00653                                 *dst = *src;
00654                                 src++;
00655                                 dst--;
00656                         }
00657                 }
00658                 rot_mat.at<double>(0,0) = -1.;
00659                 rot_mat.at<double>(0,2) = disparity_image.cols;
00660                 rot_mat.at<double>(1,1) = -1.;
00661                 rot_mat.at<double>(1,2) = disparity_image.rows;
00662         }
00663         else
00664         {
00665                 // arbitrary rotation
00666                 bool switch_aspect_ratio = !(fabs(sin(rotation_angle*CV_PI/180.)) < 0.707106781);
00667                 if (switch_aspect_ratio==false)
00668                         disparity_image_turned.create(disparity_image.rows, disparity_image.cols, disparity_image.type());              // automatically decide for landscape or portrait orientation of resulting image
00669                 else
00670                         disparity_image_turned.create(disparity_image.cols, disparity_image.rows, disparity_image.type());
00671                 if (disparity_image.type() != CV_32FC1)
00672                 {
00673                         ROS_ERROR("ImageFlip::imageCallback: Error: The image format of the color image is not CV_32FC1.\n");
00674                         return;
00675                 }
00676 
00677                 cv::Point center = cv::Point(disparity_image.cols/2, disparity_image.rows/2);
00678                 rot_mat = cv::getRotationMatrix2D(center, -rotation_angle, 1.0);
00679                 if (switch_aspect_ratio==true)
00680                 {
00681                         rot_mat.at<double>(0,2) += 0.5*(disparity_image_turned.cols-disparity_image.cols);
00682                         rot_mat.at<double>(1,2) += 0.5*(disparity_image_turned.rows-disparity_image.rows);
00683                 }
00684                 cv::warpAffine(disparity_image, disparity_image_turned, rot_mat, disparity_image_turned.size());
00685         }
00686 
00687         // publish turned image
00688         cv_bridge::CvImage cv_ptr;
00689         cv_ptr.image = disparity_image_turned;
00690         cv_ptr.encoding = disparity_image_msg->image.encoding;
00691         stereo_msgs::DisparityImage::Ptr disparity_image_turned_msg(new stereo_msgs::DisparityImage);
00692         sensor_msgs::ImagePtr disparity_image_turned_msg_image = cv_ptr.toImageMsg();
00693         disparity_image_turned_msg_image->header = disparity_image_msg->image.header;
00694         disparity_image_turned_msg->image = *disparity_image_turned_msg_image;
00695         disparity_image_turned_msg->header = disparity_image_msg->header;
00696         disparity_image_pub_.publish(disparity_image_turned_msg);
00697 
00698         // publish rotation matrix for backward transform to non-turned coordinates using a homogeneous image coordinate representation
00699         // the original, non-turned coordinates are of interest if the camera calibration shall be used (the calibration does not apply to coordinates of the turned image)
00700         cv::Mat rot33 = cv::Mat::eye(3,3,CV_64FC1);
00701         for (int r=0; r<2; ++r)
00702                 for (int c=0; c<3; ++c)
00703                         rot33.at<double>(r,c) = rot_mat.at<double>(r,c);
00704         cv::Mat rot33_inv = rot33.inv();
00705         cob_perception_msgs::Float64ArrayStamped rot33_inv_msg;
00706         rot33_inv_msg.header = disparity_image_msg->header;
00707         rot33_inv_msg.data.resize(9);
00708         for (int r=0; r<3; ++r)
00709                 for (int c=0; c<3; ++c)
00710                         rot33_inv_msg.data[r*3+c] = rot33_inv.at<double>(r,c);
00711         disparity_image_2d_transform_pub_.publish(rot33_inv_msg);
00712 }
00713 
00714 
00715 void ImageFlip::disparityConnectCB(const ros::SingleSubscriberPublisher& pub)
00716 {
00717         disparity_sub_counter_++;
00718         if (disparity_sub_counter_ == 1)
00719         {
00720                 ROS_DEBUG("ImageFlip::disparityConnectCB: Connecting disparity callback.");
00721                 disparity_image_sub_ = node_handle_.subscribe<stereo_msgs::DisparityImage>("disparityimage_in", 1, &ImageFlip::disparityCallback, this);
00722         }
00723 }
00724 
00725 void ImageFlip::disparityDisconnectCB(const ros::SingleSubscriberPublisher& pub)
00726 {
00727         disparity_sub_counter_--;
00728         if (disparity_sub_counter_ == 0)
00729         {
00730                 ROS_DEBUG("ImageFlip::disparityDisconnectCB: Disconnecting disparity callback.");
00731                 disparity_image_sub_.shutdown();
00732         }
00733 }
00734 
00735 
00736 }


cob_image_flip
Author(s): Richard Bormann
autogenerated on Fri Mar 15 2019 03:10:14