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;
00110 int nI = getOptimalDFTSize( I.cols );
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);
00115 dft(complexI, F, DFT_ROWS + DFT_COMPLEX_OUTPUT);
00116 Mat paddedJ;
00117 int nJ = getOptimalDFTSize( J.cols );
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);
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;
00135 if (angle > 180) angle = angle - 360;
00136 return std::make_pair(-angle,max/10000000);
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
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
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
00193
00194
00195
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
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