compass.cpp
Go to the documentation of this file.
00001 
00009 #include "opencv2/core/core.hpp"
00010 #include "opencv2/imgproc/imgproc.hpp"
00011 #include "opencv2/highgui/highgui.hpp"
00012 #include <cv_bridge/cv_bridge.h>
00013 #include <ros/ros.h>
00014 #include <nav_msgs/Odometry.h>
00015 #include <tf/transform_broadcaster.h>
00016 #include <sensor_msgs/image_encodings.h>
00017 #include <image_transport/image_transport.h>
00018 #include <vector>
00019 #include <cmath>
00020 
00021 #define _USE_MATH_DEFINES
00022 
00023 using namespace std;
00024 using namespace cv;
00025 
00026 namespace enc = sensor_msgs::image_encodings;
00027 
00031 class VisualCompass
00032 {
00033   ros::NodeHandle nh_;
00034   std::string compass_image; 
00035   std::string compass_output_topic; 
00036   bool textout; 
00037   double dt; 
00038   image_transport::ImageTransport it_;
00039   image_transport::Subscriber image_sub_;
00040   ros::Publisher odom_pub;
00041   static const int up = 25; 
00042   static const int down = 45; 
00043   static const int computed_rows = down-up; 
00044   Mat I; 
00045   Mat J; 
00046   double angle_deg; 
00047   ros::Time current_time, last_time;
00048 
00049 public:
00057   VisualCompass()
00058     : it_(nh_)
00059   {
00060     if (nh_.getParam("compass_image", compass_image)) {
00061       ROS_INFO("compass_image set to: %s", compass_image.c_str());
00062     } else {
00063       compass_image = "/viz/pano/image";
00064       ROS_INFO("using the default value for compass_image: %s", compass_image.c_str());
00065     }
00066     if (nh_.getParam("compass_output_topic", compass_output_topic)) {
00067       ROS_INFO("compass_output_topic set to: %s", compass_output_topic.c_str());
00068     } else {
00069       compass_output_topic = "/vcompass_pose";
00070       ROS_INFO("using the default value for compass_output_topic: %s", compass_output_topic.c_str());
00071     }
00072     if (nh_.getParam("compass_textout", textout)) {
00073       ROS_INFO("compass_textout set to: %d", textout);
00074     } else {
00075       textout = false;
00076     }
00077     
00078     angle_deg = 0;
00079 
00080     image_sub_ = it_.subscribe(compass_image, 1, &VisualCompass::imageCb, this);
00081     odom_pub = nh_.advertise<nav_msgs::Odometry>(compass_output_topic, 10);
00082   }
00086   ~VisualCompass(){};
00087 
00088 private:  
00092   double deg2rad(double deg){
00093     return (deg*M_PI) / 180;
00094   }
00095   
00102   std::pair<double, double> corel_row(const Mat & I, const Mat & J)
00103   {
00104     float max = 0;
00105     double angle = 0;
00106     Mat R;
00107     Mat F;
00108     Mat G;
00109     Mat paddedI;                          //expand input image to optimal size
00110     int nI = getOptimalDFTSize( I.cols ); // on the border add zero pixels
00111     copyMakeBorder(I, paddedI, 0, 0, 0, nI - I.cols, BORDER_CONSTANT, Scalar::all(0));
00112     Mat planesI[] = {Mat_<float>(paddedI), Mat::zeros(paddedI.size(), CV_32F)};
00113     Mat complexI;
00114     merge(planesI, 2, complexI);         // Add to the expanded another plane with zeros
00115     dft(complexI, F, DFT_ROWS + DFT_COMPLEX_OUTPUT); // this way the result may fit in the source matrix
00116     Mat paddedJ;                          //expand input image to optimal size
00117     int nJ = getOptimalDFTSize( J.cols ); // on the border add zero pixels
00118     copyMakeBorder(J, paddedJ, 0, 0, 0, nJ - J.cols, BORDER_CONSTANT, Scalar::all(0));
00119     Mat planesJ[] = {Mat_<float>(paddedJ), Mat::zeros(paddedJ.size(), CV_32F)};
00120     Mat complexJ;
00121     merge(planesJ, 2, complexJ);         // Add to the expanded another plane with zeros
00122     dft(complexJ, G, DFT_ROWS + DFT_COMPLEX_OUTPUT);
00123     mulSpectrums(F, G, R, DFT_ROWS, true );
00124     dft(R, R, DFT_REAL_OUTPUT + DFT_SCALE, I.cols);
00125 
00126     for (int i = 0; i < 2*R.cols; i = i+2)
00127     {
00128       if (R.at<float>(0,i) > max)
00129       {
00130         max = R.at<float>(0,i);
00131         angle = i;
00132       }
00133     }
00134     angle = angle*180/I.cols; // 180 because of complex input
00135     if (angle > 180) angle = angle - 360;
00136     return std::make_pair(-angle,max/10000000); // coeficient reduction for better accunacy in the following proccessing
00137   }
00138   
00144   void imageCb(const sensor_msgs::ImageConstPtr& Image)
00145   {
00146     cv_bridge::CvImagePtr cv_ptrActual;
00147     try
00148     {
00149       cv_ptrActual = cv_bridge::toCvCopy(Image, enc::MONO8);
00150       I = cv_ptrActual->image;
00151       current_time = Image->header.stamp;
00152       if (J.empty())
00153       {
00154       J = I;
00155       last_time = current_time;
00156           dt = 1;
00157     } else {
00158         dt = (current_time - last_time).toSec();
00159       }
00160     }
00161     catch (cv_bridge::Exception& e)
00162     {
00163       ROS_ERROR("cv_bridge exception: %s", e.what());
00164       return;
00165     }
00166 
00167     pair<double,double> result;
00168     double mean = 0; 
00169     double sw = 0; 
00170     
00171     for(int k = (I.rows * up / 100); k < (I.rows * down / 100); k++){
00172         result = corel_row(I.row(k),J.row(k));
00173         mean += result.first*result.second;
00174         sw += result.second;
00175     }
00176     
00177     mean = mean/sw;
00178     angle_deg += mean;
00179     if (textout) ROS_INFO("%f", angle_deg);
00180     
00181     //odometry message
00182     geometry_msgs::Quaternion odom_quat = tf::createQuaternionMsgFromYaw(deg2rad(angle_deg));
00183     nav_msgs::Odometry odom;
00184     odom.header.stamp = current_time;
00185     odom.header.frame_id = "visual_compass";
00186     
00187     //set the position
00188     odom.pose.pose.position.x = 0.0;
00189     odom.pose.pose.position.y = 0.0;
00190     odom.pose.pose.position.z = 0.0;
00191     odom.pose.pose.orientation = odom_quat;
00192 //    pose.orientation.z = sin(deg2rad(angle_deg)/2);
00193 //    pose.orientation.w = cos(deg2rad(angle_deg)/2);
00194 
00195     //set the velocity
00196     odom.child_frame_id = "base_link";
00197     odom.twist.twist.linear.x = 0;
00198     odom.twist.twist.linear.y = 0;
00199     odom.twist.twist.angular.z = deg2rad(mean)/dt;
00200 
00201     //publish the message
00202     odom_pub.publish(odom);
00203     last_time = current_time;
00204     J = I;
00205   }
00206 };
00207 
00211 int main(int argc, char **argv)
00212 {
00213     ros::init(argc, argv, "visual_compass");
00214     VisualCompass vc;
00215     ros::spin();
00216     return 0;
00217 }
00218 
 All Classes Files Functions Variables Defines


visual_compass_relase
Author(s): Tomas Nouza nouzato1@fel.cvut.cz
autogenerated on Tue Dec 3 2013 22:10:56